blob: fa45bc8f4640be89d414b6fe29f84fd782dea028 [file] [log] [blame]
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001/// Copyright 2020 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
dan sinclair0bfeaf92023-07-26 17:39:51 +000015#include "src/tint/lang/hlsl/writer/ast_printer/ast_printer.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000016
17#include <algorithm>
18#include <cmath>
19#include <functional>
20#include <iomanip>
21#include <set>
22#include <utility>
23#include <vector>
24
dan sinclair352f8c82023-07-21 00:40:07 +000025#include "src/tint/lang/core/constant/splat.h"
26#include "src/tint/lang/core/constant/value.h"
27#include "src/tint/lang/core/type/array.h"
28#include "src/tint/lang/core/type/atomic.h"
29#include "src/tint/lang/core/type/depth_multisampled_texture.h"
30#include "src/tint/lang/core/type/depth_texture.h"
31#include "src/tint/lang/core/type/multisampled_texture.h"
32#include "src/tint/lang/core/type/sampled_texture.h"
33#include "src/tint/lang/core/type/storage_texture.h"
34#include "src/tint/lang/core/type/texture_dimension.h"
dan sinclair99181d82023-07-20 01:14:15 +000035#include "src/tint/lang/wgsl/ast/call_statement.h"
36#include "src/tint/lang/wgsl/ast/id_attribute.h"
37#include "src/tint/lang/wgsl/ast/internal_attribute.h"
38#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
39#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
40#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
41#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
42#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
43#include "src/tint/lang/wgsl/ast/transform/calculate_array_length.h"
44#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
45#include "src/tint/lang/wgsl/ast/transform/decompose_memory_access.h"
46#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
47#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
48#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
49#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
50#include "src/tint/lang/wgsl/ast/transform/localize_struct_array_assignment.h"
Ben Clayton7494e832023-07-25 15:30:31 +000051#include "src/tint/lang/wgsl/ast/transform/manager.h"
dan sinclair99181d82023-07-20 01:14:15 +000052#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
53#include "src/tint/lang/wgsl/ast/transform/num_workgroups_from_uniform.h"
54#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
55#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
56#include "src/tint/lang/wgsl/ast/transform/remove_continue_in_switch.h"
57#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
58#include "src/tint/lang/wgsl/ast/transform/robustness.h"
59#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
60#include "src/tint/lang/wgsl/ast/transform/truncate_interstage_variables.h"
61#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
62#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
63#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
64#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
dan sinclaira4c17352023-07-20 09:21:10 +000065#include "src/tint/lang/wgsl/helpers/append_vector.h"
66#include "src/tint/lang/wgsl/helpers/check_supported_extensions.h"
dan sinclaird3b13692023-07-20 01:14:15 +000067#include "src/tint/lang/wgsl/sem/block_statement.h"
68#include "src/tint/lang/wgsl/sem/call.h"
69#include "src/tint/lang/wgsl/sem/function.h"
70#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
71#include "src/tint/lang/wgsl/sem/module.h"
72#include "src/tint/lang/wgsl/sem/statement.h"
73#include "src/tint/lang/wgsl/sem/struct.h"
74#include "src/tint/lang/wgsl/sem/switch_statement.h"
75#include "src/tint/lang/wgsl/sem/value_constructor.h"
76#include "src/tint/lang/wgsl/sem/value_conversion.h"
77#include "src/tint/lang/wgsl/sem/variable.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000078#include "src/tint/utils/containers/map.h"
Ben Claytonf848af22023-07-28 16:37:32 +000079#include "src/tint/utils/ice/ice.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000080#include "src/tint/utils/macros/compiler.h"
81#include "src/tint/utils/macros/defer.h"
82#include "src/tint/utils/macros/scoped_assignment.h"
83#include "src/tint/utils/rtti/switch.h"
Ben Clayton16be11d2023-08-01 00:37:35 +000084#include "src/tint/utils/strconv/float_to_string.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000085#include "src/tint/utils/text/string.h"
86#include "src/tint/utils/text/string_stream.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000087
Ben Clayton0ce9ab02022-05-05 20:23:40 +000088using namespace tint::number_suffixes; // NOLINT
89
dan sinclair0bfeaf92023-07-26 17:39:51 +000090namespace tint::hlsl::writer {
Ryan Harrisondbc13af2022-02-21 15:19:07 +000091namespace {
92
93const char kTempNamePrefix[] = "tint_tmp";
Ryan Harrisondbc13af2022-02-21 15:19:07 +000094
dan sinclairba082fd2023-02-19 04:14:19 +000095const char* image_format_to_rwtexture_type(builtin::TexelFormat image_format) {
dan sinclair41e4d9a2022-05-01 14:40:55 +000096 switch (image_format) {
dan sinclairba082fd2023-02-19 04:14:19 +000097 case builtin::TexelFormat::kBgra8Unorm:
98 case builtin::TexelFormat::kRgba8Unorm:
99 case builtin::TexelFormat::kRgba8Snorm:
100 case builtin::TexelFormat::kRgba16Float:
101 case builtin::TexelFormat::kR32Float:
102 case builtin::TexelFormat::kRg32Float:
103 case builtin::TexelFormat::kRgba32Float:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000104 return "float4";
dan sinclairba082fd2023-02-19 04:14:19 +0000105 case builtin::TexelFormat::kRgba8Uint:
106 case builtin::TexelFormat::kRgba16Uint:
107 case builtin::TexelFormat::kR32Uint:
108 case builtin::TexelFormat::kRg32Uint:
109 case builtin::TexelFormat::kRgba32Uint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000110 return "uint4";
dan sinclairba082fd2023-02-19 04:14:19 +0000111 case builtin::TexelFormat::kRgba8Sint:
112 case builtin::TexelFormat::kRgba16Sint:
113 case builtin::TexelFormat::kR32Sint:
114 case builtin::TexelFormat::kRg32Sint:
115 case builtin::TexelFormat::kRgba32Sint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000116 return "int4";
117 default:
118 return nullptr;
119 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000120}
121
dan sinclairbae54e72023-07-28 15:01:54 +0000122void PrintF32(StringStream& out, float value) {
Ben Claytone9f8b092022-06-01 13:14:39 +0000123 if (std::isinf(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000124 out << "0.0f " << (value >= 0 ? "/* inf */" : "/* -inf */");
Ben Claytone9f8b092022-06-01 13:14:39 +0000125 } else if (std::isnan(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000126 out << "0.0f /* nan */";
Ben Claytone9f8b092022-06-01 13:14:39 +0000127 } else {
dan sinclair0bfeaf92023-07-26 17:39:51 +0000128 out << tint::writer::FloatToString(value) << "f";
Ben Claytone9f8b092022-06-01 13:14:39 +0000129 }
130}
131
dan sinclairbae54e72023-07-28 15:01:54 +0000132void PrintF16(StringStream& out, float value) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000133 if (std::isinf(value)) {
134 out << "0.0h " << (value >= 0 ? "/* inf */" : "/* -inf */");
135 } else if (std::isnan(value)) {
136 out << "0.0h /* nan */";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000137 } else {
dan sinclair0bfeaf92023-07-26 17:39:51 +0000138 out << tint::writer::FloatToString(value) << "h";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000139 }
140}
141
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000142// Helper for writing " : register(RX, spaceY)", where R is the register, X is
143// the binding point binding value, and Y is the binding point group value.
144struct RegisterAndSpace {
Ben Clayton10252582023-07-25 20:53:25 +0000145 RegisterAndSpace(char r, BindingPoint bp) : reg(r), binding_point(bp) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000146
dan sinclair41e4d9a2022-05-01 14:40:55 +0000147 const char reg;
Ben Clayton10252582023-07-25 20:53:25 +0000148 BindingPoint const binding_point;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000149};
150
dan sinclairbae54e72023-07-28 15:01:54 +0000151StringStream& operator<<(StringStream& s, const RegisterAndSpace& rs) {
Peng Huangc00ff7f2023-03-31 17:55:19 +0000152 s << " : register(" << rs.reg << rs.binding_point.binding;
153 // Omit the space if it's 0, as it's the default.
154 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better compatibility.
155 if (rs.binding_point.group == 0) {
156 s << ")";
157 } else {
158 s << ", space" << rs.binding_point.group << ")";
159 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000160 return s;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000161}
162
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000163} // namespace
164
165SanitizedResult::SanitizedResult() = default;
166SanitizedResult::~SanitizedResult() = default;
167SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
168
Antonio Maiorano7eaab382022-04-11 16:33:30 +0000169SanitizedResult Sanitize(const Program* in, const Options& options) {
Ben Clayton7494e832023-07-25 15:30:31 +0000170 ast::transform::Manager manager;
171 ast::transform::DataMap data;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000172
James Priceb4acbb82023-05-11 21:27:16 +0000173 manager.Add<ast::transform::DisableUniformityAnalysis>();
James Price791b4352022-05-11 13:50:33 +0000174
Ben Clayton46ee6392022-11-09 22:04:11 +0000175 // ExpandCompoundAssignment must come before BuiltinPolyfill
James Priceb4acbb82023-05-11 21:27:16 +0000176 manager.Add<ast::transform::ExpandCompoundAssignment>();
Ben Clayton46ee6392022-11-09 22:04:11 +0000177
James Priceb4acbb82023-05-11 21:27:16 +0000178 manager.Add<ast::transform::Unshadow>(); // Must come before DirectVariableAccess
Ben Clayton03de0e82023-03-02 20:48:48 +0000179
Ben Clayton03de0e82023-03-02 20:48:48 +0000180 // LocalizeStructArrayAssignment must come after:
181 // * SimplifyPointers, because it assumes assignment to arrays in structs are
182 // done directly, not indirectly.
183 // TODO(crbug.com/tint/1340): See if we can get rid of the duplicate
184 // SimplifyPointers transform. Can't do it right now because
185 // LocalizeStructArrayAssignment introduces pointers.
James Priceb4acbb82023-05-11 21:27:16 +0000186 manager.Add<ast::transform::SimplifyPointers>();
187 manager.Add<ast::transform::LocalizeStructArrayAssignment>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000188
James Priceb4acbb82023-05-11 21:27:16 +0000189 manager.Add<ast::transform::PromoteSideEffectsToDecl>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000190
191 if (!options.disable_robustness) {
Ben Clayton8525ff22023-03-06 21:05:01 +0000192 // Robustness must come after PromoteSideEffectsToDecl
193 // Robustness must come before BuiltinPolyfill and CanonicalizeEntryPointIO
James Priceb4acbb82023-05-11 21:27:16 +0000194 manager.Add<ast::transform::Robustness>();
Jiawei Shao455e4b82023-06-10 00:32:22 +0000195
196 ast::transform::Robustness::Config config = {};
Jiawei Shao2e119632023-06-21 01:43:25 +0000197
Ben Clayton10252582023-07-25 20:53:25 +0000198 config.bindings_ignored = std::unordered_set<BindingPoint>(
Jiawei Shao455e4b82023-06-10 00:32:22 +0000199 options.binding_points_ignored_in_robustness_transform.cbegin(),
200 options.binding_points_ignored_in_robustness_transform.cend());
Jiawei Shao2e119632023-06-21 01:43:25 +0000201
202 // Direct3D guarantees to return zero for any resource that is accessed out of bounds, and
203 // according to the description of the assembly store_uav_typed, out of bounds addressing
204 // means nothing gets written to memory.
205 config.texture_action = ast::transform::Robustness::Action::kIgnore;
206
Jiawei Shao455e4b82023-06-10 00:32:22 +0000207 data.Add<ast::transform::Robustness::Config>(config);
Ben Clayton03de0e82023-03-02 20:48:48 +0000208 }
209
dan sinclair04d61c82023-04-11 23:02:45 +0000210 // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
James Priceb4acbb82023-05-11 21:27:16 +0000211 data.Add<ast::transform::MultiplanarExternalTexture::NewBindingPoints>(
dan sinclair04d61c82023-04-11 23:02:45 +0000212 options.external_texture_options.bindings_map);
James Priceb4acbb82023-05-11 21:27:16 +0000213 manager.Add<ast::transform::MultiplanarExternalTexture>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000214
dan sinclair39d40652023-03-15 13:41:46 +0000215 // BindingRemapper must come after MultiplanarExternalTexture
James Priceb4acbb82023-05-11 21:27:16 +0000216 manager.Add<ast::transform::BindingRemapper>();
217 data.Add<ast::transform::BindingRemapper::Remappings>(
dan sinclair39d40652023-03-15 13:41:46 +0000218 options.binding_remapper_options.binding_points,
219 options.binding_remapper_options.access_controls,
220 options.binding_remapper_options.allow_collisions);
221
dan sinclair41e4d9a2022-05-01 14:40:55 +0000222 { // Builtin polyfills
James Priceb4acbb82023-05-11 21:27:16 +0000223 ast::transform::BuiltinPolyfill::Builtins polyfills;
224 polyfills.acosh = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclaird23f2962022-06-28 15:27:44 +0000225 polyfills.asinh = true;
James Priceb4acbb82023-05-11 21:27:16 +0000226 polyfills.atanh = ast::transform::BuiltinPolyfill::Level::kFull;
Ben Clayton02f04d92022-11-03 19:15:17 +0000227 polyfills.bitshift_modulo = true;
Ben Clayton6dbb4632022-10-31 17:54:49 +0000228 polyfills.clamp_int = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000229 // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
230 // and `firstbithigh`.
Ben Claytoncc3f8512023-03-09 21:26:12 +0000231 polyfills.conv_f32_to_iu32 = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000232 polyfills.count_leading_zeros = true;
233 polyfills.count_trailing_zeros = true;
James Priceb4acbb82023-05-11 21:27:16 +0000234 polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000235 polyfills.first_leading_bit = true;
236 polyfills.first_trailing_bit = true;
James Priceb4acbb82023-05-11 21:27:16 +0000237 polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kFull;
Ben Clayton46ee6392022-11-09 22:04:11 +0000238 polyfills.int_div_mod = true;
Antonio Maioranoee665a42023-02-14 16:12:59 +0000239 polyfills.precise_float_mod = true;
Zhaoming Jiang04529be2023-02-27 02:59:50 +0000240 polyfills.reflect_vec2_f32 = options.polyfill_reflect_vec2_f32;
Ben Claytonc4ebf2c2022-09-22 22:59:16 +0000241 polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
James Price128980f2023-01-06 02:25:06 +0000242 polyfills.workgroup_uniform_load = true;
James Priceb4acbb82023-05-11 21:27:16 +0000243 data.Add<ast::transform::BuiltinPolyfill::Config>(polyfills);
244 manager.Add<ast::transform::BuiltinPolyfill>(); // Must come before DirectVariableAccess
dan sinclair41e4d9a2022-05-01 14:40:55 +0000245 }
Ben Clayton27aa57c2022-02-22 23:13:39 +0000246
James Priceb4acbb82023-05-11 21:27:16 +0000247 manager.Add<ast::transform::DirectVariableAccess>();
Antonio Maioranofa00fe92023-05-03 15:30:54 +0000248
dan sinclair41e4d9a2022-05-01 14:40:55 +0000249 if (!options.disable_workgroup_init) {
250 // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
251 // ZeroInitWorkgroupMemory may inject new builtin parameters.
James Priceb4acbb82023-05-11 21:27:16 +0000252 manager.Add<ast::transform::ZeroInitWorkgroupMemory>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000253 }
Ben Clayton03de0e82023-03-02 20:48:48 +0000254
255 // CanonicalizeEntryPointIO must come after Robustness
James Priceb4acbb82023-05-11 21:27:16 +0000256 manager.Add<ast::transform::CanonicalizeEntryPointIO>();
shrekshaof9c66332022-11-22 21:36:27 +0000257
shrekshao8f0607a2023-04-18 01:34:41 +0000258 if (options.truncate_interstage_variables) {
shrekshaof9c66332022-11-22 21:36:27 +0000259 // When interstage_locations is empty, it means there's no user-defined interstage variables
shrekshao8f0607a2023-04-18 01:34:41 +0000260 // being used in the next stage. Still, HLSL compiler register mismatch could happen, if
261 // there's builtin inputs used in the next stage. So we still run
262 // TruncateInterstageVariables transform.
shrekshaof9c66332022-11-22 21:36:27 +0000263
264 // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
265 // with the current stage output.
266
267 // Build the config for internal TruncateInterstageVariables transform.
James Priceb4acbb82023-05-11 21:27:16 +0000268 ast::transform::TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
shrekshaof9c66332022-11-22 21:36:27 +0000269 truncate_interstage_variables_cfg.interstage_locations =
270 std::move(options.interstage_locations);
James Priceb4acbb82023-05-11 21:27:16 +0000271 manager.Add<ast::transform::TruncateInterstageVariables>();
272 data.Add<ast::transform::TruncateInterstageVariables::Config>(
shrekshaof9c66332022-11-22 21:36:27 +0000273 std::move(truncate_interstage_variables_cfg));
274 }
275
dan sinclair41e4d9a2022-05-01 14:40:55 +0000276 // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
277 // assumes that num_workgroups builtins only appear as struct members and are
278 // only accessed directly via member accessors.
James Priceb4acbb82023-05-11 21:27:16 +0000279 manager.Add<ast::transform::NumWorkgroupsFromUniform>();
280 manager.Add<ast::transform::VectorizeScalarMatrixInitializers>();
281 manager.Add<ast::transform::SimplifyPointers>();
282 manager.Add<ast::transform::RemovePhonies>();
James Price744d0eb2022-11-09 19:58:59 +0000283
Ben Clayton03de0e82023-03-02 20:48:48 +0000284 // Build the config for the internal ArrayLengthFromUniform transform.
285 auto& array_length_from_uniform = options.array_length_from_uniform;
James Priceb4acbb82023-05-11 21:27:16 +0000286 ast::transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
Ben Clayton03de0e82023-03-02 20:48:48 +0000287 array_length_from_uniform.ubo_binding);
288 array_length_from_uniform_cfg.bindpoint_to_size_index =
289 array_length_from_uniform.bindpoint_to_size_index;
290
James Price744d0eb2022-11-09 19:58:59 +0000291 // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
292 // ExpandCompoundAssignment.
293 // TODO(crbug.com/tint/1752): This is only necessary when FXC is being used.
James Priceb4acbb82023-05-11 21:27:16 +0000294 manager.Add<ast::transform::DemoteToHelper>();
James Price744d0eb2022-11-09 19:58:59 +0000295
Ben Clayton559e5a22023-04-17 14:24:58 +0000296 // ArrayLengthFromUniform must come after SimplifyPointers as it assumes that the form of the
297 // array length argument is &var.array.
James Priceb4acbb82023-05-11 21:27:16 +0000298 manager.Add<ast::transform::ArrayLengthFromUniform>();
299 data.Add<ast::transform::ArrayLengthFromUniform::Config>(
300 std::move(array_length_from_uniform_cfg));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000301 // DecomposeMemoryAccess must come after:
Ben Clayton559e5a22023-04-17 14:24:58 +0000302 // * SimplifyPointers, as we cannot take the address of calls to
303 // DecomposeMemoryAccess::Intrinsic and we need to fold away the address-of and dereferences
304 // of `*(&(intrinsic_load()))` expressions.
dan sinclair41e4d9a2022-05-01 14:40:55 +0000305 // * RemovePhonies, as phonies can be assigned a pointer to a
306 // non-constructible buffer, or dynamic array, which DMA cannot cope with.
James Priceb4acbb82023-05-11 21:27:16 +0000307 manager.Add<ast::transform::DecomposeMemoryAccess>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000308 // CalculateArrayLength must come after DecomposeMemoryAccess, as
309 // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
310 // will be transformed by CalculateArrayLength
James Priceb4acbb82023-05-11 21:27:16 +0000311 manager.Add<ast::transform::CalculateArrayLength>();
312 manager.Add<ast::transform::PromoteInitializersToLet>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000313
James Priceb4acbb82023-05-11 21:27:16 +0000314 manager.Add<ast::transform::RemoveContinueInSwitch>();
Antonio Maioranob3497102022-03-31 15:02:25 +0000315
James Priceb4acbb82023-05-11 21:27:16 +0000316 manager.Add<ast::transform::AddEmptyEntryPoint>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000317
James Priceb4acbb82023-05-11 21:27:16 +0000318 data.Add<ast::transform::CanonicalizeEntryPointIO::Config>(
319 ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
320 data.Add<ast::transform::NumWorkgroupsFromUniform::Config>(options.root_constant_binding_point);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000321
dan sinclair41e4d9a2022-05-01 14:40:55 +0000322 SanitizedResult result;
Ben Clayton7494e832023-07-25 15:30:31 +0000323 ast::transform::DataMap outputs;
James Price0e6534e2023-05-17 01:21:45 +0000324 result.program = manager.Run(in, data, outputs);
325 if (auto* res = outputs.Get<ast::transform::ArrayLengthFromUniform::Result>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000326 result.used_array_length_from_uniform_indices = std::move(res->used_size_indices);
327 }
328 return result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000329}
330
dan sinclair0bfeaf92023-07-26 17:39:51 +0000331ASTPrinter::ASTPrinter(const Program* program) : builder_(ProgramBuilder::Wrap(program)) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000332
dan sinclair0bfeaf92023-07-26 17:39:51 +0000333ASTPrinter::~ASTPrinter() = default;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000334
dan sinclair0bfeaf92023-07-26 17:39:51 +0000335bool ASTPrinter::Generate() {
336 if (!tint::writer::CheckSupportedExtensions(
337 "HLSL", builder_.AST(), diagnostics_,
dan sinclairbae54e72023-07-28 15:01:54 +0000338 Vector{
dan sinclair0bfeaf92023-07-26 17:39:51 +0000339 builtin::Extension::kChromiumDisableUniformityAnalysis,
340 builtin::Extension::kChromiumExperimentalDp4A,
341 builtin::Extension::kChromiumExperimentalFullPtrParameters,
342 builtin::Extension::kChromiumExperimentalPushConstant,
343 builtin::Extension::kF16,
344 builtin::Extension::kChromiumInternalDualSourceBlending,
345 })) {
Ben Clayton1a567782022-10-14 13:38:27 +0000346 return false;
347 }
348
dan sinclairbae54e72023-07-28 15:01:54 +0000349 const tint::TypeInfo* last_kind = nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000350 size_t last_padding_line = 0;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000351
dan sinclair41e4d9a2022-05-01 14:40:55 +0000352 auto* mod = builder_.Sem().Module();
353 for (auto* decl : mod->DependencyOrderedDeclarations()) {
James Price98dc5a82023-02-04 12:20:55 +0000354 if (decl->IsAnyOf<ast::Alias, ast::DiagnosticDirective, ast::Enable, ast::ConstAssert>()) {
Ben Claytonb4744ac2022-08-03 07:01:08 +0000355 continue; // These are not emitted.
James Price791b4352022-05-11 13:50:33 +0000356 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000357
dan sinclair41e4d9a2022-05-01 14:40:55 +0000358 // Emit a new line between declarations if the type of declaration has
359 // changed, or we're about to emit a function
360 auto* kind = &decl->TypeInfo();
361 if (current_buffer_->lines.size() != last_padding_line) {
362 if (last_kind && (last_kind != kind || decl->Is<ast::Function>())) {
dan sinclair67a18932023-06-26 19:54:49 +0000363 Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000364 last_padding_line = current_buffer_->lines.size();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000365 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000366 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000367 last_kind = kind;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000368
dan sinclair41e4d9a2022-05-01 14:40:55 +0000369 bool ok = Switch(
370 decl,
371 [&](const ast::Variable* global) { //
372 return EmitGlobalVariable(global);
373 },
374 [&](const ast::Struct* str) {
375 auto* ty = builder_.Sem().Get(str);
dan sinclairff7cf212022-10-03 14:05:23 +0000376 auto address_space_uses = ty->AddressSpaceUsage();
377 if (address_space_uses.size() !=
dan sinclair2a651632023-02-19 04:03:55 +0000378 (address_space_uses.count(builtin::AddressSpace::kStorage) +
379 address_space_uses.count(builtin::AddressSpace::kUniform))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000380 // The structure is used as something other than a storage buffer or
381 // uniform buffer, so it needs to be emitted.
382 // Storage buffer are read and written to via a ByteAddressBuffer
383 // instead of true structure.
384 // Structures used as uniform buffer are read from an array of
385 // vectors instead of true structure.
386 return EmitStructType(current_buffer_, ty);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000387 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000388 return true;
389 },
390 [&](const ast::Function* func) {
391 if (func->IsEntryPoint()) {
392 return EmitEntryPointFunction(func);
393 }
394 return EmitFunction(func);
395 },
dan sinclair41e4d9a2022-05-01 14:40:55 +0000396 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +0000397 TINT_ICE() << "unhandled module-scope declaration: " << decl->TypeInfo().name;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000398 return false;
399 });
400
401 if (!ok) {
402 return false;
403 }
404 }
405
406 if (!helpers_.lines.empty()) {
407 current_buffer_->Insert(helpers_, 0, 0);
408 }
409
410 return true;
411}
412
dan sinclair0bfeaf92023-07-26 17:39:51 +0000413bool ASTPrinter::EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
414 const type::Vector* vec) {
dan sinclairbae54e72023-07-28 15:01:54 +0000415 auto name = tint::GetOrCreate(dynamic_vector_write_, vec, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000416 std::string fn;
417 {
dan sinclairbae54e72023-07-28 15:01:54 +0000418 StringStream ss;
dan sinclair2a651632023-02-19 04:03:55 +0000419 if (!EmitType(ss, vec, tint::builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000420 builtin::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000421 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000422 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000423 fn = UniqueIdentifier("set_" + ss.str());
424 }
425 {
dan sinclair67a18932023-06-26 19:54:49 +0000426 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000427 out << "void " << fn << "(inout ";
dan sinclair2a651632023-02-19 04:03:55 +0000428 if (!EmitTypeAndName(out, vec, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000429 builtin::Access::kUndefined, "vec")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000430 return "";
431 }
432 out << ", int idx, ";
dan sinclair2a651632023-02-19 04:03:55 +0000433 if (!EmitTypeAndName(out, vec->type(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000434 builtin::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000435 return "";
436 }
437 out << ") {";
438 }
439 {
440 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000441 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000442 switch (vec->Width()) {
443 case 2:
444 out << "vec = (idx.xx == int2(0, 1)) ? val.xx : vec;";
445 break;
446 case 3:
447 out << "vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;";
448 break;
449 case 4:
450 out << "vec = (idx.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : vec;";
451 break;
452 default:
Ben Claytonf848af22023-07-28 16:37:32 +0000453 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000454 break;
455 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000456 }
dan sinclair67a18932023-06-26 19:54:49 +0000457 Line(&helpers_) << "}";
458 Line(&helpers_);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000459 return fn;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000460 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000461
dan sinclair41e4d9a2022-05-01 14:40:55 +0000462 if (name.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000463 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000464 }
465
dan sinclair41e4d9a2022-05-01 14:40:55 +0000466 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000467
dan sinclair67a18932023-06-26 19:54:49 +0000468 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000469 out << name << "(";
470 if (!EmitExpression(out, ast_access_expr->object)) {
471 return false;
472 }
473 out << ", ";
474 if (!EmitExpression(out, ast_access_expr->index)) {
475 return false;
476 }
477 out << ", ";
478 if (!EmitExpression(out, stmt->rhs)) {
479 return false;
480 }
481 out << ");";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000482
dan sinclair41e4d9a2022-05-01 14:40:55 +0000483 return true;
484}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000485
dan sinclair0bfeaf92023-07-26 17:39:51 +0000486bool ASTPrinter::EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
487 const type::Matrix* mat) {
dan sinclairbae54e72023-07-28 15:01:54 +0000488 auto name = tint::GetOrCreate(dynamic_matrix_vector_write_, mat, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000489 std::string fn;
490 {
dan sinclairbae54e72023-07-28 15:01:54 +0000491 StringStream ss;
dan sinclair2a651632023-02-19 04:03:55 +0000492 if (!EmitType(ss, mat, tint::builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000493 builtin::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000494 return "";
495 }
496 fn = UniqueIdentifier("set_vector_" + ss.str());
497 }
498 {
dan sinclair67a18932023-06-26 19:54:49 +0000499 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000500 out << "void " << fn << "(inout ";
dan sinclair2a651632023-02-19 04:03:55 +0000501 if (!EmitTypeAndName(out, mat, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000502 builtin::Access::kUndefined, "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000503 return "";
504 }
505 out << ", int col, ";
dan sinclair2a651632023-02-19 04:03:55 +0000506 if (!EmitTypeAndName(out, mat->ColumnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000507 builtin::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000508 return "";
509 }
510 out << ") {";
511 }
512 {
513 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000514 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000515 {
516 ScopedIndent si2(&helpers_);
517 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000518 Line(&helpers_) << "case " << i << ": mat[" << i << "] = val; break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000519 }
520 }
dan sinclair67a18932023-06-26 19:54:49 +0000521 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000522 }
dan sinclair67a18932023-06-26 19:54:49 +0000523 Line(&helpers_) << "}";
524 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000525 return fn;
526 });
527
528 if (name.empty()) {
529 return false;
530 }
531
532 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
533
dan sinclair67a18932023-06-26 19:54:49 +0000534 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000535 out << name << "(";
536 if (!EmitExpression(out, ast_access_expr->object)) {
537 return false;
538 }
539 out << ", ";
540 if (!EmitExpression(out, ast_access_expr->index)) {
541 return false;
542 }
543 out << ", ";
544 if (!EmitExpression(out, stmt->rhs)) {
545 return false;
546 }
547 out << ");";
548
549 return true;
550}
551
dan sinclair0bfeaf92023-07-26 17:39:51 +0000552bool ASTPrinter::EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
553 const type::Matrix* mat) {
Antonio Maioranof031ca22023-02-02 22:16:42 +0000554 auto* lhs_row_access = stmt->lhs->As<ast::IndexAccessorExpression>();
555 auto* lhs_col_access = lhs_row_access->object->As<ast::IndexAccessorExpression>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000556
dan sinclairbae54e72023-07-28 15:01:54 +0000557 auto name = tint::GetOrCreate(dynamic_matrix_scalar_write_, mat, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000558 std::string fn;
559 {
dan sinclairbae54e72023-07-28 15:01:54 +0000560 StringStream ss;
dan sinclair2a651632023-02-19 04:03:55 +0000561 if (!EmitType(ss, mat, tint::builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000562 builtin::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000563 return "";
564 }
565 fn = UniqueIdentifier("set_scalar_" + ss.str());
566 }
567 {
dan sinclair67a18932023-06-26 19:54:49 +0000568 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000569 out << "void " << fn << "(inout ";
dan sinclair2a651632023-02-19 04:03:55 +0000570 if (!EmitTypeAndName(out, mat, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000571 builtin::Access::kUndefined, "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000572 return "";
573 }
574 out << ", int col, int row, ";
dan sinclair2a651632023-02-19 04:03:55 +0000575 if (!EmitTypeAndName(out, mat->type(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +0000576 builtin::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000577 return "";
578 }
579 out << ") {";
580 }
581 {
582 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000583 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000584 {
585 ScopedIndent si2(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000586 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000587 Line(&helpers_) << "case " << i << ":";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000588 {
589 auto vec_name = "mat[" + std::to_string(i) + "]";
590 ScopedIndent si3(&helpers_);
591 {
dan sinclair67a18932023-06-26 19:54:49 +0000592 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000593 switch (mat->rows()) {
594 case 2:
595 out << vec_name
596 << " = (row.xx == int2(0, 1)) ? val.xx : " << vec_name
597 << ";";
598 break;
599 case 3:
600 out << vec_name
601 << " = (row.xxx == int3(0, 1, 2)) ? val.xxx : " << vec_name
602 << ";";
603 break;
604 case 4:
605 out << vec_name
606 << " = (row.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : "
607 << vec_name << ";";
608 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000609 default: {
610 auto* vec = TypeOf(lhs_row_access->object)
611 ->UnwrapRef()
612 ->As<type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000613 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000614 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000615 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000616 }
617 }
dan sinclair67a18932023-06-26 19:54:49 +0000618 Line(&helpers_) << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000619 }
620 }
621 }
dan sinclair67a18932023-06-26 19:54:49 +0000622 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000623 }
dan sinclair67a18932023-06-26 19:54:49 +0000624 Line(&helpers_) << "}";
625 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000626 return fn;
627 });
628
629 if (name.empty()) {
630 return false;
631 }
632
dan sinclair67a18932023-06-26 19:54:49 +0000633 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000634 out << name << "(";
Antonio Maioranof031ca22023-02-02 22:16:42 +0000635 if (!EmitExpression(out, lhs_col_access->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000636 return false;
637 }
638 out << ", ";
639 if (!EmitExpression(out, lhs_col_access->index)) {
640 return false;
641 }
642 out << ", ";
643 if (!EmitExpression(out, lhs_row_access->index)) {
644 return false;
645 }
646 out << ", ";
647 if (!EmitExpression(out, stmt->rhs)) {
648 return false;
649 }
650 out << ");";
651
652 return true;
653}
654
dan sinclairbae54e72023-07-28 15:01:54 +0000655bool ASTPrinter::EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000656 if (!EmitExpression(out, expr->object)) {
657 return false;
658 }
659 out << "[";
660
661 if (!EmitExpression(out, expr->index)) {
662 return false;
663 }
664 out << "]";
665
666 return true;
667}
668
dan sinclairbae54e72023-07-28 15:01:54 +0000669bool ASTPrinter::EmitBitcast(StringStream& out, const ast::BitcastExpression* expr) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000670 auto* dst_type = TypeOf(expr)->UnwrapRef();
671 auto* src_type = TypeOf(expr->expr)->UnwrapRef();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000672
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000673 auto* src_el_type = src_type->DeepestElement();
674 auto* dst_el_type = dst_type->DeepestElement();
675
676 if (!dst_el_type->is_integer_scalar() && !dst_el_type->is_float_scalar()) {
dan sinclaird026e132023-04-18 19:38:25 +0000677 diagnostics_.add_error(diag::System::Writer,
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000678 "Unable to do bitcast to type " + dst_el_type->FriendlyName());
dan sinclair41e4d9a2022-05-01 14:40:55 +0000679 return false;
680 }
681
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000682 // Handle identity bitcast.
683 if (src_type == dst_type) {
684 return EmitExpression(out, expr->expr);
685 }
686
687 // Handle the f16 types using polyfill functions
688 if (src_el_type->Is<type::F16>() || dst_el_type->Is<type::F16>()) {
689 auto f16_bitcast_polyfill = [&]() {
690 if (src_el_type->Is<type::F16>()) {
691 // Source type must be vec2<f16> or vec4<f16>, since type f16 and vec3<f16> can only
692 // have identity bitcast.
693 auto* src_vec = src_type->As<type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000694 TINT_ASSERT(src_vec);
695 TINT_ASSERT(((src_vec->Width() == 2u) || (src_vec->Width() == 4u)));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000696
697 // Bitcast f16 types to others by converting the given f16 value to f32 and call
698 // f32tof16 to get the bits. This should be safe, because the convertion is precise
699 // for finite and infinite f16 value as they are exactly representable by f32, and
700 // WGSL spec allow any result if f16 value is NaN.
dan sinclairbae54e72023-07-28 15:01:54 +0000701 return tint::GetOrCreate(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000702 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
703 TextBuffer b;
704 TINT_DEFER(helpers_.Append(b));
705
706 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_from_f16"));
707 {
708 auto decl = Line(&b);
709 if (!EmitTypeAndName(decl, dst_type, builtin::AddressSpace::kUndefined,
710 builtin::Access::kUndefined, fn_name)) {
711 return "";
712 }
713 {
714 ScopedParen sp(decl);
715 if (!EmitTypeAndName(decl, src_type,
716 builtin::AddressSpace::kUndefined,
717 builtin::Access::kUndefined, "src")) {
718 return "";
719 }
720 }
721 decl << " {";
722 }
723 {
724 ScopedIndent si(&b);
725 {
726 Line(&b) << "uint" << src_vec->Width() << " r = f32tof16(float"
727 << src_vec->Width() << "(src));";
728
729 {
730 auto s = Line(&b);
731 s << "return as";
732 if (!EmitType(s, dst_el_type, builtin::AddressSpace::kUndefined,
733 builtin::Access::kReadWrite, "")) {
734 return "";
735 }
736 s << "(";
737 switch (src_vec->Width()) {
738 case 2: {
739 s << "uint((r.x & 0xffff) | ((r.y & 0xffff) << 16))";
740 break;
741 }
742 case 4: {
743 s << "uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), "
744 "(r.z & 0xffff) | ((r.w & 0xffff) << 16))";
745 break;
746 }
747 }
748 s << ");";
749 }
750 }
751 }
752 Line(&b) << "}";
753 Line(&b);
754 return fn_name;
755 });
756 } else {
757 // Destination type must be vec2<f16> or vec4<f16>.
758 auto* dst_vec = dst_type->As<type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000759 TINT_ASSERT((dst_vec && ((dst_vec->Width() == 2u) || (dst_vec->Width() == 4u)) &&
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000760 dst_el_type->Is<type::F16>()));
761 // Source type must be f32/i32/u32 or vec2<f32/i32/u32>.
762 auto* src_vec = src_type->As<type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000763 TINT_ASSERT((src_type->IsAnyOf<type::I32, type::U32, type::F32>() ||
764 (src_vec && src_vec->Width() == 2u &&
765 src_el_type->IsAnyOf<type::I32, type::U32, type::F32>())));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000766 std::string src_type_suffix = (src_vec ? "2" : "");
767
768 // Bitcast other types to f16 types by reinterpreting their bits as f16 using
769 // f16tof32, and convert the result f32 to f16. This should be safe, because the
770 // convertion is precise for finite and infinite f16 result value as they are
771 // exactly representable by f32, and WGSL spec allow any result if f16 result value
772 // would be NaN.
dan sinclairbae54e72023-07-28 15:01:54 +0000773 return tint::GetOrCreate(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000774 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
775 TextBuffer b;
776 TINT_DEFER(helpers_.Append(b));
777
778 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_to_f16"));
779 {
780 auto decl = Line(&b);
781 if (!EmitTypeAndName(decl, dst_type, builtin::AddressSpace::kUndefined,
782 builtin::Access::kUndefined, fn_name)) {
783 return "";
784 }
785 {
786 ScopedParen sp(decl);
787 if (!EmitTypeAndName(decl, src_type,
788 builtin::AddressSpace::kUndefined,
789 builtin::Access::kUndefined, "src")) {
790 return "";
791 }
792 }
793 decl << " {";
794 }
795 {
796 ScopedIndent si(&b);
797 {
798 // Convert the source to uint for f16tof32.
799 Line(&b) << "uint" << src_type_suffix << " v = asuint(src);";
800 // Reinterprete the low 16 bits and high 16 bits
801 Line(&b) << "float" << src_type_suffix
802 << " t_low = f16tof32(v & 0xffff);";
803 Line(&b) << "float" << src_type_suffix
804 << " t_high = f16tof32((v >> 16) & 0xffff);";
805 // Construct the result f16 vector
806 {
807 auto s = Line(&b);
808 s << "return ";
809 if (!EmitType(s, dst_type, builtin::AddressSpace::kUndefined,
810 builtin::Access::kReadWrite, "")) {
811 return "";
812 }
813 s << "(";
814 switch (dst_vec->Width()) {
815 case 2: {
816 s << "t_low.x, t_high.x";
817 break;
818 }
819 case 4: {
820 s << "t_low.x, t_high.x, t_low.y, t_high.y";
821 break;
822 }
823 }
824 s << ");";
825 }
826 }
827 }
828 Line(&b) << "}";
829 Line(&b);
830 return fn_name;
831 });
832 }
833 };
834
835 // Get or create the polyfill
836 auto fn = f16_bitcast_polyfill();
837 if (fn.empty()) {
838 return false;
839 }
840 // Call the polyfill
841 out << fn;
842 {
843 ScopedParen sp(out);
844 if (!EmitExpression(out, expr->expr)) {
845 return false;
846 }
847 }
848
849 return true;
850 }
851
852 // Otherwise, bitcasting between non-f16 types.
Ben Claytonf848af22023-07-28 16:37:32 +0000853 TINT_ASSERT((!src_el_type->Is<type::F16>() && !dst_el_type->Is<type::F16>()));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000854 out << "as";
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000855 if (!EmitType(out, dst_el_type, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite,
856 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000857 return false;
858 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000859 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000860 if (!EmitExpression(out, expr->expr)) {
861 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000862 }
863 out << ")";
864 return true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000865}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000866
dan sinclair0bfeaf92023-07-26 17:39:51 +0000867bool ASTPrinter::EmitAssign(const ast::AssignmentStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000868 if (auto* lhs_access = stmt->lhs->As<ast::IndexAccessorExpression>()) {
869 // BUG(crbug.com/tint/1333): work around assignment of scalar to matrices
870 // with at least one dynamic index
871 if (auto* lhs_sub_access = lhs_access->object->As<ast::IndexAccessorExpression>()) {
dan sinclair0e780da2022-12-08 22:21:24 +0000872 if (auto* mat = TypeOf(lhs_sub_access->object)->UnwrapRef()->As<type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000873 auto* rhs_row_idx_sem = builder_.Sem().GetVal(lhs_access->index);
874 auto* rhs_col_idx_sem = builder_.Sem().GetVal(lhs_sub_access->index);
Antonio Maioranof031ca22023-02-02 22:16:42 +0000875 if (!rhs_row_idx_sem->ConstantValue() || !rhs_col_idx_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000876 return EmitDynamicMatrixScalarAssignment(stmt, mat);
877 }
878 }
879 }
880 // BUG(crbug.com/tint/1333): work around assignment of vector to matrices
881 // with dynamic indices
882 const auto* lhs_access_type = TypeOf(lhs_access->object)->UnwrapRef();
dan sinclair0e780da2022-12-08 22:21:24 +0000883 if (auto* mat = lhs_access_type->As<type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000884 auto* lhs_index_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000885 if (!lhs_index_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000886 return EmitDynamicMatrixVectorAssignment(stmt, mat);
887 }
888 }
889 // BUG(crbug.com/tint/534): work around assignment to vectors with dynamic
890 // indices
dan sinclair0e780da2022-12-08 22:21:24 +0000891 if (auto* vec = lhs_access_type->As<type::Vector>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000892 auto* rhs_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000893 if (!rhs_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000894 return EmitDynamicVectorAssignment(stmt, vec);
895 }
896 }
897 }
898
dan sinclair67a18932023-06-26 19:54:49 +0000899 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000900 if (!EmitExpression(out, stmt->lhs)) {
901 return false;
902 }
903 out << " = ";
904 if (!EmitExpression(out, stmt->rhs)) {
905 return false;
906 }
907 out << ";";
908 return true;
909}
910
dan sinclairbae54e72023-07-28 15:01:54 +0000911bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000912 if (expr->op == ast::BinaryOp::kLogicalAnd || expr->op == ast::BinaryOp::kLogicalOr) {
913 auto name = UniqueIdentifier(kTempNamePrefix);
914
915 {
dan sinclair67a18932023-06-26 19:54:49 +0000916 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000917 pre << "bool " << name << " = ";
918 if (!EmitExpression(pre, expr->lhs)) {
919 return false;
920 }
921 pre << ";";
922 }
923
924 if (expr->op == ast::BinaryOp::kLogicalOr) {
dan sinclair67a18932023-06-26 19:54:49 +0000925 Line() << "if (!" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000926 } else {
dan sinclair67a18932023-06-26 19:54:49 +0000927 Line() << "if (" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000928 }
929
930 {
931 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +0000932 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000933 pre << name << " = ";
934 if (!EmitExpression(pre, expr->rhs)) {
935 return false;
936 }
937 pre << ";";
938 }
939
dan sinclair67a18932023-06-26 19:54:49 +0000940 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000941
942 out << "(" << name << ")";
943 return true;
944 }
945
946 auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
947 auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
948 // Multiplying by a matrix requires the use of `mul` in order to get the
949 // type of multiply we desire.
950 if (expr->op == ast::BinaryOp::kMultiply &&
dan sinclair0e780da2022-12-08 22:21:24 +0000951 ((lhs_type->Is<type::Vector>() && rhs_type->Is<type::Matrix>()) ||
952 (lhs_type->Is<type::Matrix>() && rhs_type->Is<type::Vector>()) ||
953 (lhs_type->Is<type::Matrix>() && rhs_type->Is<type::Matrix>()))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000954 // Matrices are transposed, so swap LHS and RHS.
955 out << "mul(";
956 if (!EmitExpression(out, expr->rhs)) {
957 return false;
958 }
959 out << ", ";
960 if (!EmitExpression(out, expr->lhs)) {
961 return false;
962 }
963 out << ")";
964
965 return true;
966 }
967
dan sinclairb2ba57b2023-02-28 15:14:09 +0000968 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000969
970 if (!EmitExpression(out, expr->lhs)) {
971 return false;
972 }
973 out << " ";
974
975 switch (expr->op) {
976 case ast::BinaryOp::kAnd:
977 out << "&";
978 break;
979 case ast::BinaryOp::kOr:
980 out << "|";
981 break;
982 case ast::BinaryOp::kXor:
983 out << "^";
984 break;
985 case ast::BinaryOp::kLogicalAnd:
986 case ast::BinaryOp::kLogicalOr: {
987 // These are both handled above.
Ben Claytonf848af22023-07-28 16:37:32 +0000988 TINT_UNREACHABLE();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000989 return false;
990 }
991 case ast::BinaryOp::kEqual:
992 out << "==";
993 break;
994 case ast::BinaryOp::kNotEqual:
995 out << "!=";
996 break;
997 case ast::BinaryOp::kLessThan:
998 out << "<";
999 break;
1000 case ast::BinaryOp::kGreaterThan:
1001 out << ">";
1002 break;
1003 case ast::BinaryOp::kLessThanEqual:
1004 out << "<=";
1005 break;
1006 case ast::BinaryOp::kGreaterThanEqual:
1007 out << ">=";
1008 break;
1009 case ast::BinaryOp::kShiftLeft:
1010 out << "<<";
1011 break;
1012 case ast::BinaryOp::kShiftRight:
1013 // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
1014 // implementation-defined behaviour for negative LHS. We may have to
1015 // generate extra code to implement WGSL-specified behaviour for negative
1016 // LHS.
1017 out << R"(>>)";
1018 break;
1019
1020 case ast::BinaryOp::kAdd:
1021 out << "+";
1022 break;
1023 case ast::BinaryOp::kSubtract:
1024 out << "-";
1025 break;
1026 case ast::BinaryOp::kMultiply:
1027 out << "*";
1028 break;
1029 case ast::BinaryOp::kDivide:
1030 out << "/";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001031 break;
1032 case ast::BinaryOp::kModulo:
1033 out << "%";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001034 break;
1035 case ast::BinaryOp::kNone:
1036 diagnostics_.add_error(diag::System::Writer, "missing binary operation type");
1037 return false;
1038 }
1039 out << " ";
1040
1041 if (!EmitExpression(out, expr->rhs)) {
1042 return false;
1043 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001044
1045 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001046}
1047
dan sinclairbae54e72023-07-28 15:01:54 +00001048bool ASTPrinter::EmitStatements(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001049 for (auto* s : stmts) {
1050 if (!EmitStatement(s)) {
1051 return false;
1052 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001053 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001054 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001055}
1056
dan sinclairbae54e72023-07-28 15:01:54 +00001057bool ASTPrinter::EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001058 ScopedIndent si(this);
1059 return EmitStatements(stmts);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001060}
1061
dan sinclair0bfeaf92023-07-26 17:39:51 +00001062bool ASTPrinter::EmitBlock(const ast::BlockStatement* stmt) {
dan sinclair67a18932023-06-26 19:54:49 +00001063 Line() << "{";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001064 if (!EmitStatementsWithIndent(stmt->statements)) {
1065 return false;
1066 }
dan sinclair67a18932023-06-26 19:54:49 +00001067 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001068 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001069}
1070
dan sinclair0bfeaf92023-07-26 17:39:51 +00001071bool ASTPrinter::EmitBreak(const ast::BreakStatement*) {
dan sinclair67a18932023-06-26 19:54:49 +00001072 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001073 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001074}
1075
dan sinclair0bfeaf92023-07-26 17:39:51 +00001076bool ASTPrinter::EmitBreakIf(const ast::BreakIfStatement* b) {
dan sinclair67a18932023-06-26 19:54:49 +00001077 auto out = Line();
dan sinclairb8b0c212022-10-20 22:45:50 +00001078 out << "if (";
1079 if (!EmitExpression(out, b->condition)) {
1080 return false;
1081 }
1082 out << ") { break; }";
1083 return true;
1084}
1085
dan sinclairbae54e72023-07-28 15:01:54 +00001086bool ASTPrinter::EmitCall(StringStream& out, const ast::CallExpression* expr) {
Ben Claytone9f8b092022-06-01 13:14:39 +00001087 auto* call = builder_.Sem().Get<sem::Call>(expr);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001088 auto* target = call->Target();
1089 return Switch(
Ben Clayton54a104e2023-02-22 20:04:40 +00001090 target, //
1091 [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
dan sinclair41e4d9a2022-05-01 14:40:55 +00001092 [&](const sem::Builtin* builtin) { return EmitBuiltinCall(out, call, builtin); },
Ben Clayton54a104e2023-02-22 20:04:40 +00001093 [&](const sem::ValueConversion* conv) { return EmitValueConversion(out, call, conv); },
1094 [&](const sem::ValueConstructor* ctor) { return EmitValueConstructor(out, call, ctor); },
dan sinclair41e4d9a2022-05-01 14:40:55 +00001095 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +00001096 TINT_ICE() << "unhandled call target: " << target->TypeInfo().name;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001097 return false;
1098 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001099}
1100
dan sinclairbae54e72023-07-28 15:01:54 +00001101bool ASTPrinter::EmitFunctionCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001102 const sem::Call* call,
1103 const sem::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001104 auto* expr = call->Declaration();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001105
James Priceb4acbb82023-05-11 21:27:16 +00001106 if (ast::HasAttribute<ast::transform::CalculateArrayLength::BufferSizeIntrinsic>(
dan sinclair41e4d9a2022-05-01 14:40:55 +00001107 func->Declaration()->attributes)) {
1108 // Special function generated by the CalculateArrayLength transform for
1109 // calling X.GetDimensions(Y)
1110 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1111 return false;
1112 }
1113 out << ".GetDimensions(";
1114 if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
1115 return false;
1116 }
1117 out << ")";
1118 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001119 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001120
James Priceb4acbb82023-05-11 21:27:16 +00001121 if (auto* intrinsic = ast::GetAttribute<ast::transform::DecomposeMemoryAccess::Intrinsic>(
dan sinclair41e4d9a2022-05-01 14:40:55 +00001122 func->Declaration()->attributes)) {
dan sinclairff7cf212022-10-03 14:05:23 +00001123 switch (intrinsic->address_space) {
dan sinclair2a651632023-02-19 04:03:55 +00001124 case builtin::AddressSpace::kUniform:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001125 return EmitUniformBufferAccess(out, expr, intrinsic);
dan sinclair2a651632023-02-19 04:03:55 +00001126 case builtin::AddressSpace::kStorage:
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001127 if (!intrinsic->IsAtomic()) {
1128 return EmitStorageBufferAccess(out, expr, intrinsic);
1129 }
1130 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001131 default:
Ben Claytonf848af22023-07-28 16:37:32 +00001132 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
1133 << intrinsic->address_space;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001134 return false;
1135 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001136 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001137
dan sinclaird026e132023-04-18 19:38:25 +00001138 out << func->Declaration()->name->symbol.Name() << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001139
1140 bool first = true;
1141 for (auto* arg : call->Arguments()) {
1142 if (!first) {
1143 out << ", ";
1144 }
1145 first = false;
1146
1147 if (!EmitExpression(out, arg->Declaration())) {
1148 return false;
1149 }
1150 }
1151
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001152 out << ")";
1153 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001154}
1155
dan sinclairbae54e72023-07-28 15:01:54 +00001156bool ASTPrinter::EmitBuiltinCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001157 const sem::Call* call,
1158 const sem::Builtin* builtin) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001159 const auto type = builtin->Type();
1160
dan sinclair41e4d9a2022-05-01 14:40:55 +00001161 auto* expr = call->Declaration();
1162 if (builtin->IsTexture()) {
1163 return EmitTextureCall(out, call, builtin);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001164 }
dan sinclair9543f742023-03-09 01:20:16 +00001165 if (type == builtin::Function::kSelect) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001166 return EmitSelectCall(out, expr);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001167 }
dan sinclair9543f742023-03-09 01:20:16 +00001168 if (type == builtin::Function::kModf) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001169 return EmitModfCall(out, expr, builtin);
1170 }
dan sinclair9543f742023-03-09 01:20:16 +00001171 if (type == builtin::Function::kFrexp) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001172 return EmitFrexpCall(out, expr, builtin);
1173 }
dan sinclair9543f742023-03-09 01:20:16 +00001174 if (type == builtin::Function::kDegrees) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001175 return EmitDegreesCall(out, expr, builtin);
1176 }
dan sinclair9543f742023-03-09 01:20:16 +00001177 if (type == builtin::Function::kRadians) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001178 return EmitRadiansCall(out, expr, builtin);
1179 }
dan sinclair9543f742023-03-09 01:20:16 +00001180 if (type == builtin::Function::kSign) {
dan sinclair70927862023-01-11 13:18:29 +00001181 return EmitSignCall(out, call, builtin);
1182 }
dan sinclair9543f742023-03-09 01:20:16 +00001183 if (type == builtin::Function::kQuantizeToF16) {
Ben Clayton2bea9052022-11-02 00:09:50 +00001184 return EmitQuantizeToF16Call(out, expr, builtin);
1185 }
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00001186 if (type == builtin::Function::kTrunc) {
1187 return EmitTruncCall(out, expr, builtin);
1188 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001189 if (builtin->IsDataPacking()) {
1190 return EmitDataPackingCall(out, expr, builtin);
1191 }
1192 if (builtin->IsDataUnpacking()) {
1193 return EmitDataUnpackingCall(out, expr, builtin);
1194 }
1195 if (builtin->IsBarrier()) {
1196 return EmitBarrierCall(out, builtin);
1197 }
1198 if (builtin->IsAtomic()) {
1199 return EmitWorkgroupAtomicCall(out, expr, builtin);
1200 }
Jiawei Shaoab975702022-05-13 00:09:56 +00001201 if (builtin->IsDP4a()) {
1202 return EmitDP4aCall(out, expr, builtin);
1203 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001204
dan sinclair41e4d9a2022-05-01 14:40:55 +00001205 auto name = generate_builtin_name(builtin);
1206 if (name.empty()) {
1207 return false;
1208 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001209
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001210 // Handle single argument builtins that only accept and return uint (not int overload). We need
1211 // to explicitly cast the return value (we also cast the arg for good measure). See
1212 // crbug.com/tint/1550
dan sinclair9543f742023-03-09 01:20:16 +00001213 if (type == builtin::Function::kCountOneBits || type == builtin::Function::kReverseBits) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001214 auto* arg = call->Arguments()[0];
Ben Clayton6c337aa2022-12-07 19:25:17 +00001215 if (arg->Type()->UnwrapRef()->is_signed_integer_scalar_or_vector()) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001216 out << "asint(" << name << "(asuint(";
1217 if (!EmitExpression(out, arg->Declaration())) {
1218 return false;
1219 }
1220 out << ")))";
1221 return true;
1222 }
1223 }
1224
dan sinclair41e4d9a2022-05-01 14:40:55 +00001225 out << name << "(";
1226
1227 bool first = true;
1228 for (auto* arg : call->Arguments()) {
1229 if (!first) {
1230 out << ", ";
1231 }
1232 first = false;
1233
1234 if (!EmitExpression(out, arg->Declaration())) {
1235 return false;
1236 }
1237 }
1238
1239 out << ")";
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001240
dan sinclair41e4d9a2022-05-01 14:40:55 +00001241 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001242}
1243
dan sinclairbae54e72023-07-28 15:01:54 +00001244bool ASTPrinter::EmitValueConversion(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001245 const sem::Call* call,
1246 const sem::ValueConversion* conv) {
dan sinclair2a651632023-02-19 04:03:55 +00001247 if (!EmitType(out, conv->Target(), builtin::AddressSpace::kUndefined,
1248 builtin::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001249 return false;
1250 }
1251 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001252
dan sinclair41e4d9a2022-05-01 14:40:55 +00001253 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1254 return false;
1255 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001256
dan sinclair41e4d9a2022-05-01 14:40:55 +00001257 out << ")";
1258 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001259}
1260
dan sinclairbae54e72023-07-28 15:01:54 +00001261bool ASTPrinter::EmitValueConstructor(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001262 const sem::Call* call,
1263 const sem::ValueConstructor* ctor) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001264 auto* type = call->Type();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001265
Ben Clayton54a104e2023-02-22 20:04:40 +00001266 // If the value constructor arguments are empty then we need to construct with the zero value
1267 // for all components.
Ben Clayton958a4642022-07-26 07:55:24 +00001268 if (call->Arguments().IsEmpty()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001269 return EmitZeroValue(out, type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001270 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001271
dan sinclair6e77b472022-10-20 13:38:28 +00001272 // Single parameter matrix initializers must be identity initializer.
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001273 // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
dan sinclair0e780da2022-12-08 22:21:24 +00001274 if (type->Is<type::Matrix>() && call->Arguments().Length() == 1) {
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001275 if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
Ben Claytonf848af22023-07-28 16:37:32 +00001276 TINT_UNREACHABLE()
dan sinclair6e77b472022-10-20 13:38:28 +00001277 << "found a single-parameter matrix initializer that is not identity initializer";
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001278 return false;
Ben Clayton3b5edf12022-05-16 21:14:11 +00001279 }
1280 }
1281
Ben Claytonbc9e4222023-04-27 18:31:44 +00001282 bool brackets = type->IsAnyOf<type::Array, type::Struct>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001283
dan sinclair41e4d9a2022-05-01 14:40:55 +00001284 // For single-value vector initializers, swizzle the scalar to the right
1285 // vector dimension using .x
1286 const bool is_single_value_vector_init = type->is_scalar_vector() &&
Ben Clayton958a4642022-07-26 07:55:24 +00001287 call->Arguments().Length() == 1 &&
Ben Clayton1416b182023-06-08 20:29:30 +00001288 ctor->Parameters()[0]->Type()->Is<type::Scalar>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001289
Ben Clayton6c098ba2022-07-14 20:46:39 +00001290 if (brackets) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001291 out << "{";
1292 } else {
dan sinclair2a651632023-02-19 04:03:55 +00001293 if (!EmitType(out, type, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite,
1294 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001295 return false;
1296 }
1297 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001298 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001299
dan sinclair41e4d9a2022-05-01 14:40:55 +00001300 if (is_single_value_vector_init) {
1301 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001302 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001303
dan sinclair41e4d9a2022-05-01 14:40:55 +00001304 bool first = true;
1305 for (auto* e : call->Arguments()) {
1306 if (!first) {
1307 out << ", ";
1308 }
1309 first = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001310
dan sinclair41e4d9a2022-05-01 14:40:55 +00001311 if (!EmitExpression(out, e->Declaration())) {
1312 return false;
1313 }
1314 }
1315
1316 if (is_single_value_vector_init) {
dan sinclair0e780da2022-12-08 22:21:24 +00001317 out << ")." << std::string(type->As<type::Vector>()->Width(), 'x');
dan sinclair41e4d9a2022-05-01 14:40:55 +00001318 }
1319
1320 out << (brackets ? "}" : ")");
1321 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001322}
1323
dan sinclair0bfeaf92023-07-26 17:39:51 +00001324bool ASTPrinter::EmitUniformBufferAccess(
dan sinclairbae54e72023-07-28 15:01:54 +00001325 StringStream& out,
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001326 const ast::CallExpression* expr,
James Priceb4acbb82023-05-11 21:27:16 +00001327 const ast::transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001328 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001329 auto* const offset = expr->args[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001330
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001331 // offset in bytes
1332 uint32_t scalar_offset_bytes = 0;
1333 // offset in uint (4 bytes)
1334 uint32_t scalar_offset_index = 0;
1335 // expression to calculate offset in bytes
1336 std::string scalar_offset_bytes_expr;
1337 // expression to calculate offset in uint, by dividing scalar_offset_bytes_expr by 4
1338 std::string scalar_offset_index_expr;
1339 // expression to calculate offset in uint, independently
1340 std::string scalar_offset_index_unified_expr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001341
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001342 // If true, use scalar_offset_index, otherwise use scalar_offset_index_expr
dan sinclair41e4d9a2022-05-01 14:40:55 +00001343 bool scalar_offset_constant = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001344
Ben Clayton1a1b5272023-02-24 17:16:55 +00001345 if (auto* val = builder_.Sem().GetVal(offset)->ConstantValue()) {
Ben Claytonf848af22023-07-28 16:37:32 +00001346 TINT_ASSERT(val->Type()->Is<type::U32>());
dan sinclairb53b8cf2022-12-15 16:25:31 +00001347 scalar_offset_bytes = static_cast<uint32_t>(val->ValueAs<AInt>());
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001348 scalar_offset_index = scalar_offset_bytes / 4; // bytes -> scalar index
dan sinclair41e4d9a2022-05-01 14:40:55 +00001349 scalar_offset_constant = true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001350 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001351
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001352 // If true, scalar_offset_bytes or scalar_offset_bytes_expr should be used, otherwise only use
1353 // scalar_offset_index or scalar_offset_index_unified_expr. Currently only loading f16 scalar
1354 // require using offset in bytes.
1355 const bool need_offset_in_bytes =
James Priceb4acbb82023-05-11 21:27:16 +00001356 intrinsic->type == ast::transform::DecomposeMemoryAccess::Intrinsic::DataType::kF16;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001357
dan sinclair41e4d9a2022-05-01 14:40:55 +00001358 if (!scalar_offset_constant) {
1359 // UBO offset not compile-time known.
1360 // Calculate the scalar offset into a temporary.
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001361 if (need_offset_in_bytes) {
1362 scalar_offset_bytes_expr = UniqueIdentifier("scalar_offset_bytes");
1363 scalar_offset_index_expr = UniqueIdentifier("scalar_offset_index");
1364 {
dan sinclair67a18932023-06-26 19:54:49 +00001365 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001366 pre << "const uint " << scalar_offset_bytes_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001367 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001368 return false;
1369 }
1370 pre << ");";
1371 }
dan sinclair67a18932023-06-26 19:54:49 +00001372 Line() << "const uint " << scalar_offset_index_expr << " = " << scalar_offset_bytes_expr
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001373 << " / 4;";
1374 } else {
1375 scalar_offset_index_unified_expr = UniqueIdentifier("scalar_offset");
dan sinclair67a18932023-06-26 19:54:49 +00001376 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001377 pre << "const uint " << scalar_offset_index_unified_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001378 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001379 return false;
1380 }
1381 pre << ") / 4;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001382 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001383 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001384
Austin Enga0e96b52023-02-18 00:39:01 +00001385 const char swizzle[] = {'x', 'y', 'z', 'w'};
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001386
James Priceb4acbb82023-05-11 21:27:16 +00001387 using Op = ast::transform::DecomposeMemoryAccess::Intrinsic::Op;
1388 using DataType = ast::transform::DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001389 switch (intrinsic->op) {
1390 case Op::kLoad: {
1391 auto cast = [&](const char* to, auto&& load) {
1392 out << to << "(";
1393 auto result = load();
1394 out << ")";
1395 return result;
1396 };
dan sinclairbae54e72023-07-28 15:01:54 +00001397 auto load_u32_to = [&](StringStream& target) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001398 target << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001399 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001400 target << "[" << (scalar_offset_index / 4) << "]."
1401 << swizzle[scalar_offset_index & 3];
dan sinclair41e4d9a2022-05-01 14:40:55 +00001402 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001403 target << "[" << scalar_offset_index_unified_expr << " / 4]["
1404 << scalar_offset_index_unified_expr << " % 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001405 }
1406 return true;
1407 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001408 auto load_u32 = [&] { return load_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001409 // Has a minimum alignment of 8 bytes, so is either .xy or .zw
dan sinclairbae54e72023-07-28 15:01:54 +00001410 auto load_vec2_u32_to = [&](StringStream& target) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001411 if (scalar_offset_constant) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001412 target << buffer << "[" << (scalar_offset_index / 4) << "]"
1413 << ((scalar_offset_index & 2) == 0 ? ".xy" : ".zw");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001414 } else {
1415 std::string ubo_load = UniqueIdentifier("ubo_load");
1416 {
dan sinclair67a18932023-06-26 19:54:49 +00001417 auto pre = Line();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001418 pre << "uint4 " << ubo_load << " = " << buffer << "["
1419 << scalar_offset_index_unified_expr << " / 4];";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001420 }
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001421 target << "((" << scalar_offset_index_unified_expr << " & 2) ? " << ubo_load
1422 << ".zw : " << ubo_load << ".xy)";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001423 }
1424 return true;
1425 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001426 auto load_vec2_u32 = [&] { return load_vec2_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001427 // vec4 has a minimum alignment of 16 bytes, easiest case
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001428 auto load_vec4_u32 = [&] {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001429 out << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001430 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001431 out << "[" << (scalar_offset_index / 4) << "]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001432 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001433 out << "[" << scalar_offset_index_unified_expr << " / 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001434 }
1435 return true;
1436 };
1437 // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001438 auto load_vec3_u32 = [&] {
1439 if (!load_vec4_u32()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001440 return false;
1441 }
1442 out << ".xyz";
1443 return true;
1444 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001445 auto load_scalar_f16 = [&] {
1446 // offset bytes = 4k, ((buffer[index].x) & 0xFFFF)
1447 // offset bytes = 4k+2, ((buffer[index].x >> 16) & 0xFFFF)
Ben Clayton1a1b5272023-02-24 17:16:55 +00001448 out << "float16_t(f16tof32(((" << buffer;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001449 if (scalar_offset_constant) {
1450 out << "[" << (scalar_offset_index / 4) << "]."
1451 << swizzle[scalar_offset_index & 3];
1452 // WGSL spec ensure little endian memory layout.
1453 if (scalar_offset_bytes % 4 == 0) {
1454 out << ") & 0xFFFF)";
1455 } else {
1456 out << " >> 16) & 0xFFFF)";
1457 }
1458 } else {
1459 out << "[" << scalar_offset_index_expr << " / 4][" << scalar_offset_index_expr
1460 << " % 4] >> (" << scalar_offset_bytes_expr
1461 << " % 4 == 0 ? 0 : 16)) & 0xFFFF)";
1462 }
1463 out << "))";
1464 return true;
1465 };
1466 auto load_vec2_f16 = [&] {
1467 // vec2<f16> is aligned to 4 bytes
1468 // Preclude code load the vec2<f16> data as a uint:
1469 // uint ubo_load = buffer[id0][id1];
1470 // Loading code convert it to vec2<f16>:
1471 // vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)),
1472 // float16_t(f16tof32(ubo_load >> 16)))
1473 std::string ubo_load = UniqueIdentifier("ubo_load");
1474 {
dan sinclair67a18932023-06-26 19:54:49 +00001475 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001476 // Load the 4 bytes f16 vector as an uint
1477 pre << "uint " << ubo_load << " = ";
1478 if (!load_u32_to(pre)) {
1479 return false;
1480 }
1481 pre << ";";
1482 }
1483 out << "vector<float16_t, 2>(float16_t(f16tof32(" << ubo_load
1484 << " & 0xFFFF)), float16_t(f16tof32(" << ubo_load << " >> 16)))";
1485 return true;
1486 };
1487 auto load_vec3_f16 = [&] {
1488 // vec3<f16> is aligned to 8 bytes
1489 // Preclude code load the vec3<f16> data as uint2 and convert its elements to
1490 // float16_t:
1491 // uint2 ubo_load = buffer[id0].xy;
1492 // /* The low 8 bits of two uint are the x and z elements of vec3<f16> */
1493 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1494 // 0xFFFF));
1495 // /* The high 8 bits of first uint is the y element of vec3<f16> */
1496 // float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
1497 // Loading code convert it to vec3<f16>:
1498 // vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1])
1499 std::string ubo_load = UniqueIdentifier("ubo_load");
1500 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1501 std::string ubo_load_y = UniqueIdentifier(ubo_load + "_y");
1502 {
dan sinclair67a18932023-06-26 19:54:49 +00001503 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001504 // Load the 8 bytes uint2 with the f16 vector at lower 6 bytes
1505 pre << "uint2 " << ubo_load << " = ";
1506 if (!load_vec2_u32_to(pre)) {
1507 return false;
1508 }
1509 pre << ";";
1510 }
1511 {
dan sinclair67a18932023-06-26 19:54:49 +00001512 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001513 pre << "vector<float16_t, 2> " << ubo_load_xz
1514 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1515 }
1516 {
dan sinclair67a18932023-06-26 19:54:49 +00001517 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001518 pre << "float16_t " << ubo_load_y << " = f16tof32(" << ubo_load
1519 << "[0] >> 16);";
1520 }
1521 out << "vector<float16_t, 3>(" << ubo_load_xz << "[0], " << ubo_load_y << ", "
1522 << ubo_load_xz << "[1])";
1523 return true;
1524 };
1525 auto load_vec4_f16 = [&] {
1526 // vec4<f16> is aligned to 8 bytes
1527 // Preclude code load the vec4<f16> data as uint2 and convert its elements to
1528 // float16_t:
1529 // uint2 ubo_load = buffer[id0].xy;
1530 // /* The low 8 bits of two uint are the x and z elements of vec4<f16> */
1531 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1532 // 0xFFFF));
1533 // /* The high 8 bits of two uint are the y and w elements of vec4<f16> */
1534 // vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >>
1535 // 16));
1536 // Loading code convert it to vec4<f16>:
1537 // vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1],
1538 // ubo_load_yw[1])
1539 std::string ubo_load = UniqueIdentifier("ubo_load");
1540 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1541 std::string ubo_load_yw = UniqueIdentifier(ubo_load + "_yw");
1542 {
dan sinclair67a18932023-06-26 19:54:49 +00001543 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001544 // Load the 8 bytes f16 vector as an uint2
1545 pre << "uint2 " << ubo_load << " = ";
1546 if (!load_vec2_u32_to(pre)) {
1547 return false;
1548 }
1549 pre << ";";
1550 }
1551 {
dan sinclair67a18932023-06-26 19:54:49 +00001552 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001553 pre << "vector<float16_t, 2> " << ubo_load_xz
1554 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1555 }
1556 {
dan sinclair67a18932023-06-26 19:54:49 +00001557 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001558 pre << "vector<float16_t, 2> " << ubo_load_yw
1559 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " >> 16));";
1560 }
1561 out << "vector<float16_t, 4>(" << ubo_load_xz << "[0], " << ubo_load_yw << "[0], "
1562 << ubo_load_xz << "[1], " << ubo_load_yw << "[1])";
1563 return true;
1564 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001565 switch (intrinsic->type) {
1566 case DataType::kU32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001567 return load_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001568 case DataType::kF32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001569 return cast("asfloat", load_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001570 case DataType::kI32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001571 return cast("asint", load_u32);
1572 case DataType::kF16:
1573 return load_scalar_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001574 case DataType::kVec2U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001575 return load_vec2_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001576 case DataType::kVec2F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001577 return cast("asfloat", load_vec2_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001578 case DataType::kVec2I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001579 return cast("asint", load_vec2_u32);
1580 case DataType::kVec2F16:
1581 return load_vec2_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001582 case DataType::kVec3U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001583 return load_vec3_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001584 case DataType::kVec3F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001585 return cast("asfloat", load_vec3_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001586 case DataType::kVec3I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001587 return cast("asint", load_vec3_u32);
1588 case DataType::kVec3F16:
1589 return load_vec3_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001590 case DataType::kVec4U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001591 return load_vec4_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001592 case DataType::kVec4F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001593 return cast("asfloat", load_vec4_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001594 case DataType::kVec4I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001595 return cast("asint", load_vec4_u32);
1596 case DataType::kVec4F16:
1597 return load_vec4_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001598 }
Ben Claytonf848af22023-07-28 16:37:32 +00001599 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1600 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001601 return false;
1602 }
1603 default:
1604 break;
1605 }
Ben Claytonf848af22023-07-28 16:37:32 +00001606 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1607 << static_cast<int>(intrinsic->op);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001608 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001609}
1610
dan sinclair0bfeaf92023-07-26 17:39:51 +00001611bool ASTPrinter::EmitStorageBufferAccess(
dan sinclairbae54e72023-07-28 15:01:54 +00001612 StringStream& out,
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001613 const ast::CallExpression* expr,
James Priceb4acbb82023-05-11 21:27:16 +00001614 const ast::transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001615 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001616 auto* const offset = expr->args[0];
Ben Clayton407137a2023-06-28 21:02:32 +00001617 auto* const value = expr->args.Length() > 1 ? expr->args[1] : nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001618
James Priceb4acbb82023-05-11 21:27:16 +00001619 using Op = ast::transform::DecomposeMemoryAccess::Intrinsic::Op;
1620 using DataType = ast::transform::DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001621 switch (intrinsic->op) {
1622 case Op::kLoad: {
1623 auto load = [&](const char* cast, int n) {
1624 if (cast) {
1625 out << cast << "(";
1626 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001627 out << buffer << ".Load";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001628 if (n > 1) {
1629 out << n;
1630 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001631 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001632 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001633 return false;
1634 }
1635 if (cast) {
1636 out << ")";
1637 }
1638 return true;
1639 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001640 // Templated load used for f16 types, requires SM6.2 or higher and DXC
1641 // Used by loading f16 types, e.g. for f16 type, set type parameter to "float16_t"
1642 // to emit `buffer.Load<float16_t>(offset)`.
1643 auto templated_load = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001644 out << buffer << ".Load<" << type << ">"; // templated load
dan sinclairb2ba57b2023-02-28 15:14:09 +00001645 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001646 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001647 return false;
1648 }
1649 return true;
1650 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001651 switch (intrinsic->type) {
1652 case DataType::kU32:
1653 return load(nullptr, 1);
1654 case DataType::kF32:
1655 return load("asfloat", 1);
1656 case DataType::kI32:
1657 return load("asint", 1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001658 case DataType::kF16:
1659 return templated_load("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001660 case DataType::kVec2U32:
1661 return load(nullptr, 2);
1662 case DataType::kVec2F32:
1663 return load("asfloat", 2);
1664 case DataType::kVec2I32:
1665 return load("asint", 2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001666 case DataType::kVec2F16:
1667 return templated_load("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001668 case DataType::kVec3U32:
1669 return load(nullptr, 3);
1670 case DataType::kVec3F32:
1671 return load("asfloat", 3);
1672 case DataType::kVec3I32:
1673 return load("asint", 3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001674 case DataType::kVec3F16:
1675 return templated_load("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001676 case DataType::kVec4U32:
1677 return load(nullptr, 4);
1678 case DataType::kVec4F32:
1679 return load("asfloat", 4);
1680 case DataType::kVec4I32:
1681 return load("asint", 4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001682 case DataType::kVec4F16:
1683 return templated_load("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001684 }
Ben Claytonf848af22023-07-28 16:37:32 +00001685 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1686 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001687 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001688 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001689
1690 case Op::kStore: {
1691 auto store = [&](int n) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001692 out << buffer << ".Store";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001693 if (n > 1) {
1694 out << n;
1695 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001696 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001697 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001698 return false;
1699 }
1700 out << ", asuint";
dan sinclairb2ba57b2023-02-28 15:14:09 +00001701 ScopedParen sp2(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001702 if (!EmitExpression(out, value)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001703 return false;
1704 }
1705 return true;
1706 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001707 // Templated stored used for f16 types, requires SM6.2 or higher and DXC
1708 // Used by storing f16 types, e.g. for f16 type, set type parameter to "float16_t"
1709 // to emit `buffer.Store<float16_t>(offset)`.
1710 auto templated_store = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001711 out << buffer << ".Store<" << type << ">"; // templated store
dan sinclairb2ba57b2023-02-28 15:14:09 +00001712 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001713 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001714 return false;
1715 }
1716 out << ", ";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001717 if (!EmitExpression(out, value)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001718 return false;
1719 }
1720 return true;
1721 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001722 switch (intrinsic->type) {
1723 case DataType::kU32:
1724 return store(1);
1725 case DataType::kF32:
1726 return store(1);
1727 case DataType::kI32:
1728 return store(1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001729 case DataType::kF16:
1730 return templated_store("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001731 case DataType::kVec2U32:
1732 return store(2);
1733 case DataType::kVec2F32:
1734 return store(2);
1735 case DataType::kVec2I32:
1736 return store(2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001737 case DataType::kVec2F16:
1738 return templated_store("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001739 case DataType::kVec3U32:
1740 return store(3);
1741 case DataType::kVec3F32:
1742 return store(3);
1743 case DataType::kVec3I32:
1744 return store(3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001745 case DataType::kVec3F16:
1746 return templated_store("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001747 case DataType::kVec4U32:
1748 return store(4);
1749 case DataType::kVec4F32:
1750 return store(4);
1751 case DataType::kVec4I32:
1752 return store(4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001753 case DataType::kVec4F16:
1754 return templated_store("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001755 }
Ben Claytonf848af22023-07-28 16:37:32 +00001756 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1757 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001758 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001759 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001760 default:
Ben Clayton1a1b5272023-02-24 17:16:55 +00001761 // Break out to error case below
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001762 // Note that atomic intrinsics are generated as functions.
1763 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001764 }
1765
Ben Claytonf848af22023-07-28 16:37:32 +00001766 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1767 << static_cast<int>(intrinsic->op);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001768 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001769}
1770
dan sinclair0bfeaf92023-07-26 17:39:51 +00001771bool ASTPrinter::EmitStorageAtomicIntrinsic(
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001772 const ast::Function* func,
James Priceb4acbb82023-05-11 21:27:16 +00001773 const ast::transform::DecomposeMemoryAccess::Intrinsic* intrinsic) {
1774 using Op = ast::transform::DecomposeMemoryAccess::Intrinsic::Op;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001775
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001776 const sem::Function* sem_func = builder_.Sem().Get(func);
1777 auto* result_ty = sem_func->ReturnType();
dan sinclaird026e132023-04-18 19:38:25 +00001778 const auto name = func->name->symbol.Name();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001779 auto& buf = *current_buffer_;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001780
dan sinclaird026e132023-04-18 19:38:25 +00001781 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001782
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001783 auto rmw = [&](const char* hlsl) -> bool {
1784 {
dan sinclair67a18932023-06-26 19:54:49 +00001785 auto fn = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001786 if (!EmitTypeAndName(fn, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001787 builtin::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001788 return false;
1789 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001790 fn << "(uint offset, ";
dan sinclair2a651632023-02-19 04:03:55 +00001791 if (!EmitTypeAndName(fn, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001792 builtin::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001793 return false;
1794 }
1795 fn << ") {";
1796 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001797
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001798 buf.IncrementIndent();
1799 TINT_DEFER({
1800 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001801 Line(&buf) << "}";
1802 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001803 });
1804
1805 {
dan sinclair67a18932023-06-26 19:54:49 +00001806 auto l = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001807 if (!EmitTypeAndName(l, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001808 builtin::Access::kUndefined, "original_value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001809 return false;
1810 }
1811 l << " = 0;";
1812 }
1813 {
dan sinclair67a18932023-06-26 19:54:49 +00001814 auto l = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001815 l << buffer << "." << hlsl << "(offset, ";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001816 if (intrinsic->op == Op::kAtomicSub) {
1817 l << "-";
1818 }
1819 l << "value, original_value);";
1820 }
dan sinclair67a18932023-06-26 19:54:49 +00001821 Line(&buf) << "return original_value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001822 return true;
1823 };
1824
1825 switch (intrinsic->op) {
1826 case Op::kAtomicAdd:
1827 return rmw("InterlockedAdd");
1828
1829 case Op::kAtomicSub:
1830 // Use add with the operand negated.
1831 return rmw("InterlockedAdd");
1832
1833 case Op::kAtomicMax:
1834 return rmw("InterlockedMax");
1835
1836 case Op::kAtomicMin:
1837 return rmw("InterlockedMin");
1838
1839 case Op::kAtomicAnd:
1840 return rmw("InterlockedAnd");
1841
1842 case Op::kAtomicOr:
1843 return rmw("InterlockedOr");
1844
1845 case Op::kAtomicXor:
1846 return rmw("InterlockedXor");
1847
1848 case Op::kAtomicExchange:
1849 return rmw("InterlockedExchange");
1850
1851 case Op::kAtomicLoad: {
1852 // HLSL does not have an InterlockedLoad, so we emulate it with
1853 // InterlockedOr using 0 as the OR value
dan sinclair41e4d9a2022-05-01 14:40:55 +00001854 {
dan sinclair67a18932023-06-26 19:54:49 +00001855 auto fn = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001856 if (!EmitTypeAndName(fn, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001857 builtin::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001858 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001859 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001860 fn << "(uint offset) {";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001861 }
1862
1863 buf.IncrementIndent();
1864 TINT_DEFER({
1865 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001866 Line(&buf) << "}";
1867 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001868 });
1869
1870 {
dan sinclair67a18932023-06-26 19:54:49 +00001871 auto l = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001872 if (!EmitTypeAndName(l, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001873 builtin::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001874 return false;
1875 }
1876 l << " = 0;";
1877 }
1878
dan sinclair67a18932023-06-26 19:54:49 +00001879 Line(&buf) << buffer << ".InterlockedOr(offset, 0, value);";
1880 Line(&buf) << "return value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001881 return true;
1882 }
1883 case Op::kAtomicStore: {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001884 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001885 // HLSL does not have an InterlockedStore, so we emulate it with
1886 // InterlockedExchange and discard the returned value
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001887 {
dan sinclair67a18932023-06-26 19:54:49 +00001888 auto fn = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001889 fn << "void " << name << "(uint offset, ";
dan sinclair2a651632023-02-19 04:03:55 +00001890 if (!EmitTypeAndName(fn, value_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001891 builtin::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001892 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001893 }
1894 fn << ") {";
1895 }
1896
1897 buf.IncrementIndent();
1898 TINT_DEFER({
1899 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001900 Line(&buf) << "}";
1901 Line(&buf);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001902 });
1903
1904 {
dan sinclair67a18932023-06-26 19:54:49 +00001905 auto l = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001906 if (!EmitTypeAndName(l, value_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001907 builtin::Access::kUndefined, "ignored")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001908 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001909 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001910 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001911 }
dan sinclair67a18932023-06-26 19:54:49 +00001912 Line(&buf) << buffer << ".InterlockedExchange(offset, value, ignored);";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001913 return true;
1914 }
1915 case Op::kAtomicCompareExchangeWeak: {
Ben Clayton4b8a3d32023-06-13 16:55:57 +00001916 if (!EmitStructType(&helpers_, result_ty->As<type::Struct>())) {
1917 return false;
1918 }
1919
Ben Clayton1a1b5272023-02-24 17:16:55 +00001920 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001921 // NOTE: We don't need to emit the return type struct here as DecomposeMemoryAccess
1922 // already added it to the AST, and it should have already been emitted by now.
dan sinclair41e4d9a2022-05-01 14:40:55 +00001923 {
dan sinclair67a18932023-06-26 19:54:49 +00001924 auto fn = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001925 if (!EmitTypeAndName(fn, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001926 builtin::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001927 return false;
1928 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001929 fn << "(uint offset, ";
dan sinclair2a651632023-02-19 04:03:55 +00001930 if (!EmitTypeAndName(fn, value_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001931 builtin::Access::kUndefined, "compare")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001932 return false;
1933 }
1934 fn << ", ";
dan sinclair2a651632023-02-19 04:03:55 +00001935 if (!EmitTypeAndName(fn, value_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001936 builtin::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001937 return false;
1938 }
1939 fn << ") {";
1940 }
1941
1942 buf.IncrementIndent();
1943 TINT_DEFER({
1944 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001945 Line(&buf) << "}";
1946 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001947 });
1948
1949 { // T result = {0};
dan sinclair67a18932023-06-26 19:54:49 +00001950 auto l = Line(&buf);
dan sinclair2a651632023-02-19 04:03:55 +00001951 if (!EmitTypeAndName(l, result_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001952 builtin::Access::kUndefined, "result")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001953 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001954 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001955 l << "=";
1956 if (!EmitZeroValue(l, result_ty)) {
1957 return false;
1958 }
1959 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001960 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001961
dan sinclair67a18932023-06-26 19:54:49 +00001962 Line(&buf) << buffer
Ben Clayton1a1b5272023-02-24 17:16:55 +00001963 << ".InterlockedCompareExchange(offset, compare, value, result.old_value);";
dan sinclair67a18932023-06-26 19:54:49 +00001964 Line(&buf) << "result.exchanged = result.old_value == compare;";
1965 Line(&buf) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001966
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001967 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001968 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001969 default:
1970 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001971 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001972
Ben Claytonf848af22023-07-28 16:37:32 +00001973 TINT_UNREACHABLE() << "unsupported atomic DecomposeMemoryAccess::Intrinsic::Op: "
1974 << static_cast<int>(intrinsic->op);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001975 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001976}
1977
dan sinclairbae54e72023-07-28 15:01:54 +00001978bool ASTPrinter::EmitWorkgroupAtomicCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001979 const ast::CallExpression* expr,
1980 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001981 std::string result = UniqueIdentifier("atomic_result");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001982
dan sinclaird37ecf92022-12-08 16:39:59 +00001983 if (!builtin->ReturnType()->Is<type::Void>()) {
dan sinclair67a18932023-06-26 19:54:49 +00001984 auto pre = Line();
dan sinclair2a651632023-02-19 04:03:55 +00001985 if (!EmitTypeAndName(pre, builtin->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00001986 builtin::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001987 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001988 }
1989 pre << " = ";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001990 if (!EmitZeroValue(pre, builtin->ReturnType())) {
1991 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001992 }
1993 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001994 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001995
dan sinclair41e4d9a2022-05-01 14:40:55 +00001996 auto call = [&](const char* name) {
dan sinclair67a18932023-06-26 19:54:49 +00001997 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001998 pre << name;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001999
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002000 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002001 ScopedParen sp(pre);
Ben Clayton783b1692022-08-02 17:03:35 +00002002 for (size_t i = 0; i < expr->args.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002003 auto* arg = expr->args[i];
2004 if (i > 0) {
2005 pre << ", ";
2006 }
dan sinclair9543f742023-03-09 01:20:16 +00002007 if (i == 1 && builtin->Type() == builtin::Function::kAtomicSub) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002008 // Sub uses InterlockedAdd with the operand negated.
2009 pre << "-";
2010 }
2011 if (!EmitExpression(pre, arg)) {
2012 return false;
2013 }
2014 }
2015
2016 pre << ", " << result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002017 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002018
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002019 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002020
dan sinclair41e4d9a2022-05-01 14:40:55 +00002021 out << result;
2022 return true;
2023 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002024
dan sinclair41e4d9a2022-05-01 14:40:55 +00002025 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002026 case builtin::Function::kAtomicLoad: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002027 // HLSL does not have an InterlockedLoad, so we emulate it with
2028 // InterlockedOr using 0 as the OR value
dan sinclair67a18932023-06-26 19:54:49 +00002029 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002030 pre << "InterlockedOr";
2031 {
2032 ScopedParen sp(pre);
2033 if (!EmitExpression(pre, expr->args[0])) {
2034 return false;
2035 }
2036 pre << ", 0, " << result;
2037 }
2038 pre << ";";
2039
2040 out << result;
2041 return true;
2042 }
dan sinclair9543f742023-03-09 01:20:16 +00002043 case builtin::Function::kAtomicStore: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002044 // HLSL does not have an InterlockedStore, so we emulate it with
2045 // InterlockedExchange and discard the returned value
2046 { // T result = 0;
dan sinclair67a18932023-06-26 19:54:49 +00002047 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002048 auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
dan sinclair2a651632023-02-19 04:03:55 +00002049 if (!EmitTypeAndName(pre, value_ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00002050 builtin::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002051 return false;
2052 }
2053 pre << " = ";
2054 if (!EmitZeroValue(pre, value_ty)) {
2055 return false;
2056 }
2057 pre << ";";
2058 }
2059
2060 out << "InterlockedExchange";
2061 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00002062 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002063 if (!EmitExpression(out, expr->args[0])) {
2064 return false;
2065 }
2066 out << ", ";
2067 if (!EmitExpression(out, expr->args[1])) {
2068 return false;
2069 }
2070 out << ", " << result;
2071 }
2072 return true;
2073 }
dan sinclair9543f742023-03-09 01:20:16 +00002074 case builtin::Function::kAtomicCompareExchangeWeak: {
Ben Claytonbc9e4222023-04-27 18:31:44 +00002075 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<type::Struct>())) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002076 return false;
2077 }
2078
dan sinclair41e4d9a2022-05-01 14:40:55 +00002079 auto* dest = expr->args[0];
2080 auto* compare_value = expr->args[1];
2081 auto* value = expr->args[2];
2082
2083 std::string compare = UniqueIdentifier("atomic_compare_value");
2084
2085 { // T compare_value = <compare_value>;
dan sinclair67a18932023-06-26 19:54:49 +00002086 auto pre = Line();
Antonio Maioranof99671b2022-06-23 13:14:54 +00002087 if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
dan sinclair2a651632023-02-19 04:03:55 +00002088 builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
dan sinclair61c16eb2023-01-21 23:44:38 +00002089 compare)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002090 return false;
2091 }
2092 pre << " = ";
2093 if (!EmitExpression(pre, compare_value)) {
2094 return false;
2095 }
2096 pre << ";";
2097 }
2098
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002099 { // InterlockedCompareExchange(dst, compare, value, result.old_value);
dan sinclair67a18932023-06-26 19:54:49 +00002100 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002101 pre << "InterlockedCompareExchange";
2102 {
2103 ScopedParen sp(pre);
2104 if (!EmitExpression(pre, dest)) {
2105 return false;
2106 }
2107 pre << ", " << compare << ", ";
2108 if (!EmitExpression(pre, value)) {
2109 return false;
2110 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002111 pre << ", " << result << ".old_value";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002112 }
2113 pre << ";";
2114 }
2115
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002116 // result.exchanged = result.old_value == compare;
dan sinclair67a18932023-06-26 19:54:49 +00002117 Line() << result << ".exchanged = " << result << ".old_value == " << compare << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002118
2119 out << result;
2120 return true;
2121 }
2122
dan sinclair9543f742023-03-09 01:20:16 +00002123 case builtin::Function::kAtomicAdd:
2124 case builtin::Function::kAtomicSub:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002125 return call("InterlockedAdd");
2126
dan sinclair9543f742023-03-09 01:20:16 +00002127 case builtin::Function::kAtomicMax:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002128 return call("InterlockedMax");
2129
dan sinclair9543f742023-03-09 01:20:16 +00002130 case builtin::Function::kAtomicMin:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002131 return call("InterlockedMin");
2132
dan sinclair9543f742023-03-09 01:20:16 +00002133 case builtin::Function::kAtomicAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002134 return call("InterlockedAnd");
2135
dan sinclair9543f742023-03-09 01:20:16 +00002136 case builtin::Function::kAtomicOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002137 return call("InterlockedOr");
2138
dan sinclair9543f742023-03-09 01:20:16 +00002139 case builtin::Function::kAtomicXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002140 return call("InterlockedXor");
2141
dan sinclair9543f742023-03-09 01:20:16 +00002142 case builtin::Function::kAtomicExchange:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002143 return call("InterlockedExchange");
2144
2145 default:
2146 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002147 }
2148
Ben Claytonf848af22023-07-28 16:37:32 +00002149 TINT_UNREACHABLE() << "unsupported atomic builtin: " << builtin->Type();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002150 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002151}
2152
dan sinclairbae54e72023-07-28 15:01:54 +00002153bool ASTPrinter::EmitSelectCall(StringStream& out, const ast::CallExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002154 auto* expr_false = expr->args[0];
2155 auto* expr_true = expr->args[1];
2156 auto* expr_cond = expr->args[2];
dan sinclairb2ba57b2023-02-28 15:14:09 +00002157 ScopedParen paren(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002158 if (!EmitExpression(out, expr_cond)) {
2159 return false;
2160 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002161
dan sinclair41e4d9a2022-05-01 14:40:55 +00002162 out << " ? ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002163
dan sinclair41e4d9a2022-05-01 14:40:55 +00002164 if (!EmitExpression(out, expr_true)) {
2165 return false;
2166 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002167
dan sinclair41e4d9a2022-05-01 14:40:55 +00002168 out << " : ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002169
dan sinclair41e4d9a2022-05-01 14:40:55 +00002170 if (!EmitExpression(out, expr_false)) {
2171 return false;
2172 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002173
dan sinclair41e4d9a2022-05-01 14:40:55 +00002174 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002175}
2176
dan sinclairbae54e72023-07-28 15:01:54 +00002177bool ASTPrinter::EmitModfCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002178 const ast::CallExpression* expr,
2179 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002180 return CallBuiltinHelper(
2181 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2182 auto* ty = builtin->Parameters()[0]->Type();
2183 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002184
dan sinclair41e4d9a2022-05-01 14:40:55 +00002185 std::string width;
dan sinclair0e780da2022-12-08 22:21:24 +00002186 if (auto* vec = ty->As<type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002187 width = std::to_string(vec->Width());
2188 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002189
dan sinclair41e4d9a2022-05-01 14:40:55 +00002190 // Emit the builtin return type unique to this overload. This does not
2191 // exist in the AST, so it will not be generated in Generate().
Ben Claytonbc9e4222023-04-27 18:31:44 +00002192 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002193 return false;
2194 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002195
dan sinclair41e4d9a2022-05-01 14:40:55 +00002196 {
dan sinclair67a18932023-06-26 19:54:49 +00002197 auto l = Line(b);
dan sinclair2a651632023-02-19 04:03:55 +00002198 if (!EmitType(l, builtin->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00002199 builtin::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002200 return false;
2201 }
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002202 l << " result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002203 }
dan sinclair67a18932023-06-26 19:54:49 +00002204 Line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
2205 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002206 return true;
2207 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002208}
2209
dan sinclairbae54e72023-07-28 15:01:54 +00002210bool ASTPrinter::EmitFrexpCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002211 const ast::CallExpression* expr,
2212 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002213 return CallBuiltinHelper(
2214 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2215 auto* ty = builtin->Parameters()[0]->Type();
2216 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002217
dan sinclair41e4d9a2022-05-01 14:40:55 +00002218 std::string width;
dan sinclair0e780da2022-12-08 22:21:24 +00002219 if (auto* vec = ty->As<type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002220 width = std::to_string(vec->Width());
2221 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002222
dan sinclair41e4d9a2022-05-01 14:40:55 +00002223 // Emit the builtin return type unique to this overload. This does not
2224 // exist in the AST, so it will not be generated in Generate().
Ben Claytonbc9e4222023-04-27 18:31:44 +00002225 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002226 return false;
2227 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002228
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002229 std::string member_type;
Ben Clayton471a0152023-06-09 10:07:16 +00002230 if (Is<type::F16>(ty->DeepestElement())) {
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002231 member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
2232 } else {
2233 member_type = "float" + width;
2234 }
2235
dan sinclair67a18932023-06-26 19:54:49 +00002236 Line(b) << member_type << " exp;";
2237 Line(b) << member_type << " fract = sign(" << in << ") * frexp(" << in << ", exp);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002238 {
dan sinclair67a18932023-06-26 19:54:49 +00002239 auto l = Line(b);
dan sinclair2a651632023-02-19 04:03:55 +00002240 if (!EmitType(l, builtin->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00002241 builtin::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002242 return false;
2243 }
Ben Clayton10fae7a2022-11-14 15:29:29 +00002244 l << " result = {fract, int" << width << "(exp)};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002245 }
dan sinclair67a18932023-06-26 19:54:49 +00002246 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002247 return true;
2248 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002249}
2250
dan sinclairbae54e72023-07-28 15:01:54 +00002251bool ASTPrinter::EmitDegreesCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002252 const ast::CallExpression* expr,
2253 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002254 return CallBuiltinHelper(out, expr, builtin,
2255 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002256 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002257 << sem::kRadToDeg << ";";
2258 return true;
2259 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002260}
2261
dan sinclairbae54e72023-07-28 15:01:54 +00002262bool ASTPrinter::EmitRadiansCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002263 const ast::CallExpression* expr,
2264 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002265 return CallBuiltinHelper(out, expr, builtin,
2266 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002267 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002268 << sem::kDegToRad << ";";
2269 return true;
2270 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002271}
2272
dan sinclair70927862023-01-11 13:18:29 +00002273// The HLSL `sign` method always returns an `int` result (scalar or vector). In WGSL the result is
2274// expected to be the same type as the argument. This injects a cast to the expected WGSL result
2275// type after the call to `sign`.
dan sinclairbae54e72023-07-28 15:01:54 +00002276bool ASTPrinter::EmitSignCall(StringStream& out, const sem::Call* call, const sem::Builtin*) {
dan sinclair70927862023-01-11 13:18:29 +00002277 auto* arg = call->Arguments()[0];
dan sinclair2a651632023-02-19 04:03:55 +00002278 if (!EmitType(out, arg->Type(), builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00002279 "")) {
dan sinclair70927862023-01-11 13:18:29 +00002280 return false;
2281 }
2282 out << "(sign(";
2283 if (!EmitExpression(out, arg->Declaration())) {
2284 return false;
2285 }
2286 out << "))";
2287 return true;
2288}
2289
dan sinclairbae54e72023-07-28 15:01:54 +00002290bool ASTPrinter::EmitQuantizeToF16Call(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002291 const ast::CallExpression* expr,
2292 const sem::Builtin* builtin) {
Antonio Maiorano51249b82023-03-31 08:00:33 +00002293 // Cast to f16 and back
Ben Clayton2bea9052022-11-02 00:09:50 +00002294 std::string width;
dan sinclair0e780da2022-12-08 22:21:24 +00002295 if (auto* vec = builtin->ReturnType()->As<type::Vector>()) {
Ben Clayton2bea9052022-11-02 00:09:50 +00002296 width = std::to_string(vec->Width());
2297 }
Antonio Maiorano51249b82023-03-31 08:00:33 +00002298 out << "f16tof32(f32tof16"
2299 << "(";
Ben Clayton2bea9052022-11-02 00:09:50 +00002300 if (!EmitExpression(out, expr->args[0])) {
2301 return false;
2302 }
2303 out << "))";
2304 return true;
2305}
2306
dan sinclairbae54e72023-07-28 15:01:54 +00002307bool ASTPrinter::EmitTruncCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002308 const ast::CallExpression* expr,
2309 const sem::Builtin* builtin) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002310 // HLSL's trunc is broken for very large/small float values.
2311 // See crbug.com/tint/1883
2312 return CallBuiltinHelper( //
2313 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2314 // value < 0 ? ceil(value) : floor(value)
dan sinclair67a18932023-06-26 19:54:49 +00002315 Line(b) << "return " << params[0] << " < 0 ? ceil(" << params[0] << ") : floor("
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002316 << params[0] << ");";
2317 return true;
2318 });
2319}
2320
dan sinclairbae54e72023-07-28 15:01:54 +00002321bool ASTPrinter::EmitDataPackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002322 const ast::CallExpression* expr,
2323 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002324 return CallBuiltinHelper(
2325 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2326 uint32_t dims = 2;
2327 bool is_signed = false;
2328 uint32_t scale = 65535;
dan sinclair9543f742023-03-09 01:20:16 +00002329 if (builtin->Type() == builtin::Function::kPack4X8Snorm ||
2330 builtin->Type() == builtin::Function::kPack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002331 dims = 4;
2332 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002333 }
dan sinclair9543f742023-03-09 01:20:16 +00002334 if (builtin->Type() == builtin::Function::kPack4X8Snorm ||
2335 builtin->Type() == builtin::Function::kPack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002336 is_signed = true;
2337 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002338 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002339 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002340 case builtin::Function::kPack4X8Snorm:
2341 case builtin::Function::kPack4X8Unorm:
2342 case builtin::Function::kPack2X16Snorm:
2343 case builtin::Function::kPack2X16Unorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002344 {
dan sinclair67a18932023-06-26 19:54:49 +00002345 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002346 l << (is_signed ? "" : "u") << "int" << dims
2347 << " i = " << (is_signed ? "" : "u") << "int" << dims << "(round(clamp("
2348 << params[0] << ", " << (is_signed ? "-1.0" : "0.0") << ", 1.0) * "
2349 << scale << ".0))";
2350 if (is_signed) {
2351 l << " & " << (dims == 4 ? "0xff" : "0xffff");
2352 }
2353 l << ";";
2354 }
2355 {
dan sinclair67a18932023-06-26 19:54:49 +00002356 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002357 l << "return ";
2358 if (is_signed) {
2359 l << "asuint";
2360 }
2361 l << "(i.x | i.y << " << (32 / dims);
2362 if (dims == 4) {
2363 l << " | i.z << 16 | i.w << 24";
2364 }
2365 l << ");";
2366 }
2367 break;
2368 }
dan sinclair9543f742023-03-09 01:20:16 +00002369 case builtin::Function::kPack2X16Float: {
dan sinclair67a18932023-06-26 19:54:49 +00002370 Line(b) << "uint2 i = f32tof16(" << params[0] << ");";
2371 Line(b) << "return i.x | (i.y << 16);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002372 break;
2373 }
2374 default:
2375 diagnostics_.add_error(diag::System::Writer,
2376 "Internal error: unhandled data packing builtin");
2377 return false;
2378 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002379
dan sinclair41e4d9a2022-05-01 14:40:55 +00002380 return true;
2381 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002382}
2383
dan sinclairbae54e72023-07-28 15:01:54 +00002384bool ASTPrinter::EmitDataUnpackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002385 const ast::CallExpression* expr,
2386 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002387 return CallBuiltinHelper(
2388 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2389 uint32_t dims = 2;
2390 bool is_signed = false;
2391 uint32_t scale = 65535;
dan sinclair9543f742023-03-09 01:20:16 +00002392 if (builtin->Type() == builtin::Function::kUnpack4X8Snorm ||
2393 builtin->Type() == builtin::Function::kUnpack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002394 dims = 4;
2395 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002396 }
dan sinclair9543f742023-03-09 01:20:16 +00002397 if (builtin->Type() == builtin::Function::kUnpack4X8Snorm ||
2398 builtin->Type() == builtin::Function::kUnpack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002399 is_signed = true;
2400 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002401 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002402 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002403 case builtin::Function::kUnpack4X8Snorm:
2404 case builtin::Function::kUnpack2X16Snorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002405 Line(b) << "int j = int(" << params[0] << ");";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002406 { // Perform sign extension on the converted values.
dan sinclair67a18932023-06-26 19:54:49 +00002407 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002408 l << "int" << dims << " i = int" << dims << "(";
2409 if (dims == 2) {
2410 l << "j << 16, j) >> 16";
2411 } else {
2412 l << "j << 24, j << 16, j << 8, j) >> 24";
2413 }
2414 l << ";";
2415 }
dan sinclair67a18932023-06-26 19:54:49 +00002416 Line(b) << "return clamp(float" << dims << "(i) / " << scale << ".0, "
dan sinclair41e4d9a2022-05-01 14:40:55 +00002417 << (is_signed ? "-1.0" : "0.0") << ", 1.0);";
2418 break;
2419 }
dan sinclair9543f742023-03-09 01:20:16 +00002420 case builtin::Function::kUnpack4X8Unorm:
2421 case builtin::Function::kUnpack2X16Unorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002422 Line(b) << "uint j = " << params[0] << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002423 {
dan sinclair67a18932023-06-26 19:54:49 +00002424 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002425 l << "uint" << dims << " i = uint" << dims << "(";
2426 l << "j & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
2427 if (dims == 4) {
2428 l << "(j >> " << (32 / dims) << ") & 0xff, (j >> 16) & 0xff, j >> 24";
2429 } else {
2430 l << "j >> " << (32 / dims);
2431 }
2432 l << ");";
2433 }
dan sinclair67a18932023-06-26 19:54:49 +00002434 Line(b) << "return float" << dims << "(i) / " << scale << ".0;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002435 break;
2436 }
dan sinclair9543f742023-03-09 01:20:16 +00002437 case builtin::Function::kUnpack2X16Float:
dan sinclair67a18932023-06-26 19:54:49 +00002438 Line(b) << "uint i = " << params[0] << ";";
2439 Line(b) << "return f16tof32(uint2(i & 0xffff, i >> 16));";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002440 break;
2441 default:
2442 diagnostics_.add_error(diag::System::Writer,
2443 "Internal error: unhandled data packing builtin");
2444 return false;
2445 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002446
dan sinclair41e4d9a2022-05-01 14:40:55 +00002447 return true;
2448 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002449}
2450
dan sinclairbae54e72023-07-28 15:01:54 +00002451bool ASTPrinter::EmitDP4aCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002452 const ast::CallExpression* expr,
2453 const sem::Builtin* builtin) {
Jiawei Shaoab975702022-05-13 00:09:56 +00002454 // TODO(crbug.com/tint/1497): support the polyfill version of DP4a functions.
2455 return CallBuiltinHelper(
2456 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2457 std::string functionName;
2458 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002459 case builtin::Function::kDot4I8Packed:
dan sinclair67a18932023-06-26 19:54:49 +00002460 Line(b) << "int accumulator = 0;";
Jiawei Shaoab975702022-05-13 00:09:56 +00002461 functionName = "dot4add_i8packed";
2462 break;
dan sinclair9543f742023-03-09 01:20:16 +00002463 case builtin::Function::kDot4U8Packed:
dan sinclair67a18932023-06-26 19:54:49 +00002464 Line(b) << "uint accumulator = 0u;";
Jiawei Shaoab975702022-05-13 00:09:56 +00002465 functionName = "dot4add_u8packed";
2466 break;
2467 default:
2468 diagnostics_.add_error(diag::System::Writer,
2469 "Internal error: unhandled DP4a builtin");
2470 return false;
2471 }
dan sinclair67a18932023-06-26 19:54:49 +00002472 Line(b) << "return " << functionName << "(" << params[0] << ", " << params[1]
Jiawei Shao1c759212022-05-15 13:53:21 +00002473 << ", accumulator);";
Jiawei Shaoab975702022-05-13 00:09:56 +00002474
2475 return true;
2476 });
2477}
2478
dan sinclairbae54e72023-07-28 15:01:54 +00002479bool ASTPrinter::EmitBarrierCall(StringStream& out, const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002480 // TODO(crbug.com/tint/661): Combine sequential barriers to a single
2481 // instruction.
dan sinclair9543f742023-03-09 01:20:16 +00002482 if (builtin->Type() == builtin::Function::kWorkgroupBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002483 out << "GroupMemoryBarrierWithGroupSync()";
dan sinclair9543f742023-03-09 01:20:16 +00002484 } else if (builtin->Type() == builtin::Function::kStorageBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002485 out << "DeviceMemoryBarrierWithGroupSync()";
2486 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00002487 TINT_UNREACHABLE() << "unexpected barrier builtin type " << builtin::str(builtin->Type());
dan sinclair41e4d9a2022-05-01 14:40:55 +00002488 return false;
2489 }
2490 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002491}
2492
dan sinclairbae54e72023-07-28 15:01:54 +00002493bool ASTPrinter::EmitTextureCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002494 const sem::Call* call,
2495 const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002496 using Usage = sem::ParameterUsage;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002497
dan sinclair41e4d9a2022-05-01 14:40:55 +00002498 auto& signature = builtin->Signature();
2499 auto* expr = call->Declaration();
2500 auto arguments = expr->args;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002501
dan sinclair41e4d9a2022-05-01 14:40:55 +00002502 // Returns the argument with the given usage
2503 auto arg = [&](Usage usage) {
2504 int idx = signature.IndexOf(usage);
dan sinclair3a2a2792022-06-29 14:38:15 +00002505 return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002506 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002507
dan sinclair41e4d9a2022-05-01 14:40:55 +00002508 auto* texture = arg(Usage::kTexture);
Ben Clayton884f9522023-01-12 22:52:57 +00002509 if (TINT_UNLIKELY(!texture)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002510 TINT_ICE() << "missing texture argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002511 return false;
2512 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002513
dan sinclair4595fb72022-12-08 14:14:10 +00002514 auto* texture_type = TypeOf(texture)->UnwrapRef()->As<type::Texture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002515
dan sinclair41e4d9a2022-05-01 14:40:55 +00002516 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002517 case builtin::Function::kTextureDimensions:
2518 case builtin::Function::kTextureNumLayers:
2519 case builtin::Function::kTextureNumLevels:
2520 case builtin::Function::kTextureNumSamples: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002521 // All of these builtins use the GetDimensions() method on the texture
2522 bool is_ms =
dan sinclair4595fb72022-12-08 14:14:10 +00002523 texture_type->IsAnyOf<type::MultisampledTexture, type::DepthMultisampledTexture>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002524 int num_dimensions = 0;
2525 std::string swizzle;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002526
dan sinclair41e4d9a2022-05-01 14:40:55 +00002527 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002528 case builtin::Function::kTextureDimensions:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002529 switch (texture_type->dim()) {
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002530 case type::TextureDimension::kNone:
Ben Claytonf848af22023-07-28 16:37:32 +00002531 TINT_ICE() << "texture dimension is kNone";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002532 return false;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002533 case type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002534 num_dimensions = 1;
2535 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002536 case type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002537 num_dimensions = is_ms ? 3 : 2;
2538 swizzle = is_ms ? ".xy" : "";
2539 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002540 case type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002541 num_dimensions = is_ms ? 4 : 3;
2542 swizzle = ".xy";
2543 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002544 case type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002545 num_dimensions = 3;
2546 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002547 case type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002548 num_dimensions = 2;
2549 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002550 case type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002551 num_dimensions = 3;
2552 swizzle = ".xy";
2553 break;
2554 }
2555 break;
dan sinclair9543f742023-03-09 01:20:16 +00002556 case builtin::Function::kTextureNumLayers:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002557 switch (texture_type->dim()) {
2558 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002559 TINT_ICE() << "texture dimension is not arrayed";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002560 return false;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002561 case type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002562 num_dimensions = is_ms ? 4 : 3;
2563 swizzle = ".z";
2564 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002565 case type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002566 num_dimensions = 3;
2567 swizzle = ".z";
2568 break;
2569 }
2570 break;
dan sinclair9543f742023-03-09 01:20:16 +00002571 case builtin::Function::kTextureNumLevels:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002572 switch (texture_type->dim()) {
2573 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002574 TINT_ICE() << "texture dimension does not support mips";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002575 return false;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002576 case type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002577 num_dimensions = 2;
2578 swizzle = ".y";
2579 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002580 case type::TextureDimension::k2d:
2581 case type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002582 num_dimensions = 3;
2583 swizzle = ".z";
2584 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002585 case type::TextureDimension::k2dArray:
2586 case type::TextureDimension::k3d:
2587 case type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002588 num_dimensions = 4;
2589 swizzle = ".w";
2590 break;
2591 }
2592 break;
dan sinclair9543f742023-03-09 01:20:16 +00002593 case builtin::Function::kTextureNumSamples:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002594 switch (texture_type->dim()) {
2595 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002596 TINT_ICE() << "texture dimension does not support multisampling";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002597 return false;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002598 case type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002599 num_dimensions = 3;
2600 swizzle = ".z";
2601 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00002602 case type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002603 num_dimensions = 4;
2604 swizzle = ".w";
2605 break;
2606 }
2607 break;
2608 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002609 TINT_ICE() << "unexpected builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002610 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002611 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002612
2613 auto* level_arg = arg(Usage::kLevel);
2614
2615 if (level_arg) {
2616 // `NumberOfLevels` is a non-optional argument if `MipLevel` was passed.
2617 // Increment the number of dimensions for the temporary vector to
2618 // accommodate this.
2619 num_dimensions++;
2620
2621 // If the swizzle was empty, the expression will evaluate to the whole
2622 // vector. As we've grown the vector by one element, we now need to
2623 // swizzle to keep the result expression equivalent.
2624 if (swizzle.empty()) {
2625 static constexpr const char* swizzles[] = {"", ".x", ".xy", ".xyz"};
2626 swizzle = swizzles[num_dimensions - 1];
2627 }
2628 }
2629
Ben Clayton884f9522023-01-12 22:52:57 +00002630 if (TINT_UNLIKELY(num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002631 TINT_ICE() << "Texture query builtin temporary vector has " << num_dimensions
2632 << " dimensions";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002633 return false;
2634 }
2635
2636 // Declare a variable to hold the queried texture info
2637 auto dims = UniqueIdentifier(kTempNamePrefix);
2638 if (num_dimensions == 1) {
dan sinclair67a18932023-06-26 19:54:49 +00002639 Line() << "uint " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002640 } else {
dan sinclair67a18932023-06-26 19:54:49 +00002641 Line() << "uint" << num_dimensions << " " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002642 }
2643
2644 { // texture.GetDimensions(...)
dan sinclair67a18932023-06-26 19:54:49 +00002645 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002646 if (!EmitExpression(pre, texture)) {
2647 return false;
2648 }
2649 pre << ".GetDimensions(";
2650
2651 if (level_arg) {
2652 if (!EmitExpression(pre, level_arg)) {
2653 return false;
2654 }
2655 pre << ", ";
dan sinclair9543f742023-03-09 01:20:16 +00002656 } else if (builtin->Type() == builtin::Function::kTextureNumLevels) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002657 pre << "0, ";
2658 }
2659
2660 if (num_dimensions == 1) {
2661 pre << dims;
2662 } else {
2663 static constexpr char xyzw[] = {'x', 'y', 'z', 'w'};
Ben Clayton884f9522023-01-12 22:52:57 +00002664 if (TINT_UNLIKELY(num_dimensions < 0 || num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002665 TINT_ICE() << "vector dimensions are " << num_dimensions;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002666 return false;
2667 }
2668 for (int i = 0; i < num_dimensions; i++) {
2669 if (i > 0) {
2670 pre << ", ";
2671 }
2672 pre << dims << "." << xyzw[i];
2673 }
2674 }
2675
2676 pre << ");";
2677 }
2678
2679 // The out parameters of the GetDimensions() call is now in temporary
2680 // `dims` variable. This may be packed with other data, so the final
2681 // expression may require a swizzle.
2682 out << dims << swizzle;
2683 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002684 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002685 default:
2686 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002687 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002688
Austin Eng86a617f2022-05-19 20:08:19 +00002689 if (!EmitExpression(out, texture)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002690 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002691 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002692
2693 // If pack_level_in_coords is true, then the mip level will be appended as the
2694 // last value of the coordinates argument. If the WGSL builtin overload does
2695 // not have a level parameter and pack_level_in_coords is true, then a zero
2696 // mip level will be inserted.
2697 bool pack_level_in_coords = false;
2698
2699 uint32_t hlsl_ret_width = 4u;
2700
2701 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002702 case builtin::Function::kTextureSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002703 out << ".Sample(";
2704 break;
dan sinclair9543f742023-03-09 01:20:16 +00002705 case builtin::Function::kTextureSampleBias:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002706 out << ".SampleBias(";
2707 break;
dan sinclair9543f742023-03-09 01:20:16 +00002708 case builtin::Function::kTextureSampleLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002709 out << ".SampleLevel(";
2710 break;
dan sinclair9543f742023-03-09 01:20:16 +00002711 case builtin::Function::kTextureSampleGrad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002712 out << ".SampleGrad(";
2713 break;
dan sinclair9543f742023-03-09 01:20:16 +00002714 case builtin::Function::kTextureSampleCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002715 out << ".SampleCmp(";
2716 hlsl_ret_width = 1;
2717 break;
dan sinclair9543f742023-03-09 01:20:16 +00002718 case builtin::Function::kTextureSampleCompareLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002719 out << ".SampleCmpLevelZero(";
2720 hlsl_ret_width = 1;
2721 break;
dan sinclair9543f742023-03-09 01:20:16 +00002722 case builtin::Function::kTextureLoad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002723 out << ".Load(";
2724 // Multisampled textures do not support mip-levels.
dan sinclair4595fb72022-12-08 14:14:10 +00002725 if (!texture_type->Is<type::MultisampledTexture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002726 pack_level_in_coords = true;
2727 }
2728 break;
dan sinclair9543f742023-03-09 01:20:16 +00002729 case builtin::Function::kTextureGather:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002730 out << ".Gather";
2731 if (builtin->Parameters()[0]->Usage() == sem::ParameterUsage::kComponent) {
dan sinclair5addefb2022-12-14 20:46:32 +00002732 switch (call->Arguments()[0]->ConstantValue()->ValueAs<AInt>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002733 case 0:
2734 out << "Red";
2735 break;
2736 case 1:
2737 out << "Green";
2738 break;
2739 case 2:
2740 out << "Blue";
2741 break;
2742 case 3:
2743 out << "Alpha";
2744 break;
2745 }
2746 }
2747 out << "(";
2748 break;
dan sinclair9543f742023-03-09 01:20:16 +00002749 case builtin::Function::kTextureGatherCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002750 out << ".GatherCmp(";
2751 break;
dan sinclair9543f742023-03-09 01:20:16 +00002752 case builtin::Function::kTextureStore:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002753 out << "[";
2754 break;
2755 default:
2756 diagnostics_.add_error(diag::System::Writer,
2757 "Internal compiler error: Unhandled texture builtin '" +
2758 std::string(builtin->str()) + "'");
2759 return false;
2760 }
2761
2762 if (auto* sampler = arg(Usage::kSampler)) {
Austin Eng86a617f2022-05-19 20:08:19 +00002763 if (!EmitExpression(out, sampler)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002764 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002765 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002766 out << ", ";
2767 }
2768
2769 auto* param_coords = arg(Usage::kCoords);
Ben Clayton884f9522023-01-12 22:52:57 +00002770 if (TINT_UNLIKELY(!param_coords)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002771 TINT_ICE() << "missing coords argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002772 return false;
2773 }
2774
2775 auto emit_vector_appended_with_i32_zero = [&](const ast::Expression* vector) {
dan sinclaird37ecf92022-12-08 16:39:59 +00002776 auto* i32 = builder_.create<type::I32>();
Ben Clayton0ce9ab02022-05-05 20:23:40 +00002777 auto* zero = builder_.Expr(0_i);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002778 auto* stmt = builder_.Sem().Get(vector)->Stmt();
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002779 builder_.Sem().Add(zero, builder_.create<sem::ValueExpression>(
2780 zero, i32, sem::EvaluationStage::kRuntime, stmt,
2781 /* constant_value */ nullptr,
2782 /* has_side_effects */ false));
dan sinclair0bfeaf92023-07-26 17:39:51 +00002783 auto* packed = tint::writer::AppendVector(&builder_, vector, zero);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002784 return EmitExpression(out, packed->Declaration());
2785 };
2786
2787 auto emit_vector_appended_with_level = [&](const ast::Expression* vector) {
2788 if (auto* level = arg(Usage::kLevel)) {
dan sinclair0bfeaf92023-07-26 17:39:51 +00002789 auto* packed = tint::writer::AppendVector(&builder_, vector, level);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002790 return EmitExpression(out, packed->Declaration());
2791 }
2792 return emit_vector_appended_with_i32_zero(vector);
2793 };
2794
2795 if (auto* array_index = arg(Usage::kArrayIndex)) {
2796 // Array index needs to be appended to the coordinates.
dan sinclair0bfeaf92023-07-26 17:39:51 +00002797 auto* packed = tint::writer::AppendVector(&builder_, param_coords, array_index);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002798 if (pack_level_in_coords) {
2799 // Then mip level needs to be appended to the coordinates.
2800 if (!emit_vector_appended_with_level(packed->Declaration())) {
2801 return false;
2802 }
2803 } else {
2804 if (!EmitExpression(out, packed->Declaration())) {
2805 return false;
2806 }
2807 }
2808 } else if (pack_level_in_coords) {
2809 // Mip level needs to be appended to the coordinates.
2810 if (!emit_vector_appended_with_level(param_coords)) {
2811 return false;
2812 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002813 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002814 if (!EmitExpression(out, param_coords)) {
2815 return false;
2816 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002817 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002818
dan sinclair41e4d9a2022-05-01 14:40:55 +00002819 for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
2820 Usage::kSampleIndex, Usage::kOffset}) {
2821 if (usage == Usage::kLevel && pack_level_in_coords) {
2822 continue; // mip level already packed in coordinates.
2823 }
2824 if (auto* e = arg(usage)) {
2825 out << ", ";
2826 if (!EmitExpression(out, e)) {
2827 return false;
2828 }
2829 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002830 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002831
dan sinclair9543f742023-03-09 01:20:16 +00002832 if (builtin->Type() == builtin::Function::kTextureStore) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002833 out << "] = ";
2834 if (!EmitExpression(out, arg(Usage::kValue))) {
2835 return false;
2836 }
2837 } else {
2838 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002839
dan sinclair41e4d9a2022-05-01 14:40:55 +00002840 // If the builtin return type does not match the number of elements of the
2841 // HLSL builtin, we need to swizzle the expression to generate the correct
2842 // number of components.
2843 uint32_t wgsl_ret_width = 1;
dan sinclair0e780da2022-12-08 22:21:24 +00002844 if (auto* vec = builtin->ReturnType()->As<type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002845 wgsl_ret_width = vec->Width();
2846 }
2847 if (wgsl_ret_width < hlsl_ret_width) {
2848 out << ".";
2849 for (uint32_t i = 0; i < wgsl_ret_width; i++) {
2850 out << "xyz"[i];
2851 }
2852 }
Ben Clayton884f9522023-01-12 22:52:57 +00002853 if (TINT_UNLIKELY(wgsl_ret_width > hlsl_ret_width)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002854 TINT_ICE() << "WGSL return width (" << wgsl_ret_width
2855 << ") is wider than HLSL return width (" << hlsl_ret_width << ") for "
2856 << builtin->Type();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002857 return false;
2858 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002859 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002860
dan sinclair41e4d9a2022-05-01 14:40:55 +00002861 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002862}
2863
dan sinclair0bfeaf92023-07-26 17:39:51 +00002864std::string ASTPrinter::generate_builtin_name(const sem::Builtin* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002865 switch (builtin->Type()) {
dan sinclair9543f742023-03-09 01:20:16 +00002866 case builtin::Function::kAbs:
2867 case builtin::Function::kAcos:
2868 case builtin::Function::kAll:
2869 case builtin::Function::kAny:
2870 case builtin::Function::kAsin:
2871 case builtin::Function::kAtan:
2872 case builtin::Function::kAtan2:
2873 case builtin::Function::kCeil:
2874 case builtin::Function::kClamp:
2875 case builtin::Function::kCos:
2876 case builtin::Function::kCosh:
2877 case builtin::Function::kCross:
2878 case builtin::Function::kDeterminant:
2879 case builtin::Function::kDistance:
2880 case builtin::Function::kDot:
2881 case builtin::Function::kExp:
2882 case builtin::Function::kExp2:
2883 case builtin::Function::kFloor:
2884 case builtin::Function::kFrexp:
2885 case builtin::Function::kLdexp:
2886 case builtin::Function::kLength:
2887 case builtin::Function::kLog:
2888 case builtin::Function::kLog2:
2889 case builtin::Function::kMax:
2890 case builtin::Function::kMin:
2891 case builtin::Function::kModf:
2892 case builtin::Function::kNormalize:
2893 case builtin::Function::kPow:
2894 case builtin::Function::kReflect:
2895 case builtin::Function::kRefract:
2896 case builtin::Function::kRound:
2897 case builtin::Function::kSaturate:
2898 case builtin::Function::kSin:
2899 case builtin::Function::kSinh:
2900 case builtin::Function::kSqrt:
2901 case builtin::Function::kStep:
2902 case builtin::Function::kTan:
2903 case builtin::Function::kTanh:
2904 case builtin::Function::kTranspose:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002905 return builtin->str();
dan sinclair9543f742023-03-09 01:20:16 +00002906 case builtin::Function::kCountOneBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00002907 return "countbits";
dan sinclair9543f742023-03-09 01:20:16 +00002908 case builtin::Function::kDpdx:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002909 return "ddx";
dan sinclair9543f742023-03-09 01:20:16 +00002910 case builtin::Function::kDpdxCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002911 return "ddx_coarse";
dan sinclair9543f742023-03-09 01:20:16 +00002912 case builtin::Function::kDpdxFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002913 return "ddx_fine";
dan sinclair9543f742023-03-09 01:20:16 +00002914 case builtin::Function::kDpdy:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002915 return "ddy";
dan sinclair9543f742023-03-09 01:20:16 +00002916 case builtin::Function::kDpdyCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002917 return "ddy_coarse";
dan sinclair9543f742023-03-09 01:20:16 +00002918 case builtin::Function::kDpdyFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002919 return "ddy_fine";
dan sinclair9543f742023-03-09 01:20:16 +00002920 case builtin::Function::kFaceForward:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002921 return "faceforward";
dan sinclair9543f742023-03-09 01:20:16 +00002922 case builtin::Function::kFract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002923 return "frac";
dan sinclair9543f742023-03-09 01:20:16 +00002924 case builtin::Function::kFma:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002925 return "mad";
dan sinclair9543f742023-03-09 01:20:16 +00002926 case builtin::Function::kFwidth:
2927 case builtin::Function::kFwidthCoarse:
2928 case builtin::Function::kFwidthFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002929 return "fwidth";
dan sinclair9543f742023-03-09 01:20:16 +00002930 case builtin::Function::kInverseSqrt:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002931 return "rsqrt";
dan sinclair9543f742023-03-09 01:20:16 +00002932 case builtin::Function::kMix:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002933 return "lerp";
dan sinclair9543f742023-03-09 01:20:16 +00002934 case builtin::Function::kReverseBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00002935 return "reversebits";
dan sinclair9543f742023-03-09 01:20:16 +00002936 case builtin::Function::kSmoothstep:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002937 return "smoothstep";
2938 default:
2939 diagnostics_.add_error(diag::System::Writer,
2940 "Unknown builtin method: " + std::string(builtin->str()));
2941 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002942
dan sinclair41e4d9a2022-05-01 14:40:55 +00002943 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002944}
2945
dan sinclair0bfeaf92023-07-26 17:39:51 +00002946bool ASTPrinter::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002947 auto* stmt = s->body[case_idx];
dan sinclairf148f082022-10-19 15:55:02 +00002948 auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
2949 for (auto* selector : sem->Selectors()) {
dan sinclair67a18932023-06-26 19:54:49 +00002950 auto out = Line();
dan sinclairf148f082022-10-19 15:55:02 +00002951 if (selector->IsDefault()) {
2952 out << "default";
2953 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002954 out << "case ";
Ben Clayton329dfd72022-11-23 00:05:05 +00002955 if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002956 return false;
2957 }
dan sinclairf148f082022-10-19 15:55:02 +00002958 }
2959 out << ":";
2960 if (selector == sem->Selectors().back()) {
2961 out << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002962 }
2963 }
2964
dan sinclair67a18932023-06-26 19:54:49 +00002965 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002966 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00002967 DecrementIndent();
2968 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002969 });
2970
2971 // Emit the case statement
2972 if (!EmitStatements(stmt->body->statements)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002973 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002974 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002975
dan sinclairbae54e72023-07-28 15:01:54 +00002976 if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
dan sinclair67a18932023-06-26 19:54:49 +00002977 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002978 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002979
dan sinclair41e4d9a2022-05-01 14:40:55 +00002980 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002981}
2982
dan sinclair0bfeaf92023-07-26 17:39:51 +00002983bool ASTPrinter::EmitContinue(const ast::ContinueStatement*) {
dan sinclair4b88dbc2022-06-16 15:27:38 +00002984 if (!emit_continuing_ || !emit_continuing_()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002985 return false;
2986 }
dan sinclair67a18932023-06-26 19:54:49 +00002987 Line() << "continue;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002988 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002989}
2990
dan sinclair0bfeaf92023-07-26 17:39:51 +00002991bool ASTPrinter::EmitDiscard(const ast::DiscardStatement*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002992 // TODO(dsinclair): Verify this is correct when the discard semantics are
2993 // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
dan sinclair67a18932023-06-26 19:54:49 +00002994 Line() << "discard;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002995 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002996}
2997
dan sinclairbae54e72023-07-28 15:01:54 +00002998bool ASTPrinter::EmitExpression(StringStream& out, const ast::Expression* expr) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +00002999 if (auto* sem = builder_.Sem().GetVal(expr)) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003000 if (auto* constant = sem->ConstantValue()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003001 bool is_variable_initializer = false;
3002 if (auto* stmt = sem->Stmt()) {
3003 if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
3004 is_variable_initializer = decl->variable->initializer == expr;
3005 }
3006 }
3007 return EmitConstant(out, constant, is_variable_initializer);
Ben Claytone9f8b092022-06-01 13:14:39 +00003008 }
3009 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003010 return Switch(
Ben Claytonb90b6bf2022-08-23 16:23:05 +00003011 expr, //
3012 [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
3013 [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
3014 [&](const ast::BitcastExpression* b) { return EmitBitcast(out, b); },
3015 [&](const ast::CallExpression* c) { return EmitCall(out, c); },
3016 [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
3017 [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
3018 [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
3019 [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); },
3020 [&](Default) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003021 diagnostics_.add_error(diag::System::Writer, "unknown expression type: " +
3022 std::string(expr->TypeInfo().name));
3023 return false;
3024 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003025}
3026
dan sinclairbae54e72023-07-28 15:01:54 +00003027bool ASTPrinter::EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr) {
dan sinclaird026e132023-04-18 19:38:25 +00003028 out << expr->identifier->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003029 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003030}
3031
dan sinclair0bfeaf92023-07-26 17:39:51 +00003032bool ASTPrinter::EmitIf(const ast::IfStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003033 {
dan sinclair67a18932023-06-26 19:54:49 +00003034 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003035 out << "if (";
3036 if (!EmitExpression(out, stmt->condition)) {
3037 return false;
3038 }
3039 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003040 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003041
dan sinclair41e4d9a2022-05-01 14:40:55 +00003042 if (!EmitStatementsWithIndent(stmt->body->statements)) {
James Price26ebe5e2022-04-29 00:14:53 +00003043 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003044 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003045
dan sinclair41e4d9a2022-05-01 14:40:55 +00003046 if (stmt->else_statement) {
dan sinclair67a18932023-06-26 19:54:49 +00003047 Line() << "} else {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003048 if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
3049 if (!EmitStatementsWithIndent(block->statements)) {
3050 return false;
3051 }
3052 } else {
dan sinclairbae54e72023-07-28 15:01:54 +00003053 if (!EmitStatementsWithIndent(Vector{stmt->else_statement})) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003054 return false;
3055 }
3056 }
3057 }
dan sinclair67a18932023-06-26 19:54:49 +00003058 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003059
3060 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003061}
3062
dan sinclair0bfeaf92023-07-26 17:39:51 +00003063bool ASTPrinter::EmitFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003064 auto* sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003065
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003066 // Emit storage atomic helpers
3067 if (auto* intrinsic =
James Priceb4acbb82023-05-11 21:27:16 +00003068 ast::GetAttribute<ast::transform::DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
dan sinclair2a651632023-02-19 04:03:55 +00003069 if (intrinsic->address_space == builtin::AddressSpace::kStorage && intrinsic->IsAtomic()) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003070 if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
3071 return false;
3072 }
3073 }
3074 return true;
3075 }
3076
dan sinclair41e4d9a2022-05-01 14:40:55 +00003077 if (ast::HasAttribute<ast::InternalAttribute>(func->attributes)) {
3078 // An internal function. Do not emit.
3079 return true;
3080 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003081
dan sinclair41e4d9a2022-05-01 14:40:55 +00003082 {
dan sinclair67a18932023-06-26 19:54:49 +00003083 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00003084 auto name = func->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003085 // If the function returns an array, then we need to declare a typedef for
3086 // this.
dan sinclair946858a2022-12-08 22:21:24 +00003087 if (sem->ReturnType()->Is<type::Array>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003088 auto typedef_name = UniqueIdentifier(name + "_ret");
dan sinclair67a18932023-06-26 19:54:49 +00003089 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003090 pre << "typedef ";
dan sinclair2a651632023-02-19 04:03:55 +00003091 if (!EmitTypeAndName(pre, sem->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003092 builtin::Access::kReadWrite, typedef_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003093 return false;
3094 }
3095 pre << ";";
3096 out << typedef_name;
3097 } else {
dan sinclair2a651632023-02-19 04:03:55 +00003098 if (!EmitType(out, sem->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003099 builtin::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003100 return false;
3101 }
3102 }
3103
3104 out << " " << name << "(";
3105
3106 bool first = true;
3107
3108 for (auto* v : sem->Parameters()) {
3109 if (!first) {
3110 out << ", ";
3111 }
3112 first = false;
3113
3114 auto const* type = v->Type();
dan sinclair2a651632023-02-19 04:03:55 +00003115 auto address_space = builtin::AddressSpace::kUndefined;
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003116 auto access = builtin::Access::kUndefined;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003117
dan sinclair4d56b482022-12-08 17:50:50 +00003118 if (auto* ptr = type->As<type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003119 type = ptr->StoreType();
dan sinclairff7cf212022-10-03 14:05:23 +00003120 switch (ptr->AddressSpace()) {
dan sinclair2a651632023-02-19 04:03:55 +00003121 case builtin::AddressSpace::kStorage:
3122 case builtin::AddressSpace::kUniform:
Ben Clayton2032d032022-06-15 19:32:37 +00003123 // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
3124 // storage buffers and uniform buffers down into transform-generated
3125 // functions. In this situation we want to generate the parameter without an
dan sinclairff7cf212022-10-03 14:05:23 +00003126 // 'inout', using the address space and access from the pointer.
3127 address_space = ptr->AddressSpace();
Ben Clayton2032d032022-06-15 19:32:37 +00003128 access = ptr->Access();
3129 break;
3130 default:
3131 // Transform regular WGSL pointer parameters in to `inout` parameters.
3132 out << "inout ";
3133 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003134 }
3135
Ben Clayton1b90f932023-02-18 12:37:34 +00003136 // Note: WGSL only allows for AddressSpace::kUndefined on parameters, however
dan sinclair41e4d9a2022-05-01 14:40:55 +00003137 // the sanitizer transforms generates load / store functions for storage
3138 // or uniform buffers. These functions have a buffer parameter with
dan sinclairff7cf212022-10-03 14:05:23 +00003139 // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
dan sinclair41e4d9a2022-05-01 14:40:55 +00003140 // correctly translate the parameter to a [RW]ByteAddressBuffer for
3141 // storage buffers and a uint4[N] for uniform buffers.
dan sinclairff7cf212022-10-03 14:05:23 +00003142 if (!EmitTypeAndName(out, type, address_space, access,
dan sinclaird026e132023-04-18 19:38:25 +00003143 v->Declaration()->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003144 return false;
3145 }
3146 }
3147 out << ") {";
3148 }
3149
dan sinclaird37ecf92022-12-08 16:39:59 +00003150 if (sem->DiscardStatement() && !sem->ReturnType()->Is<type::Void>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003151 // BUG(crbug.com/tint/1081): work around non-void functions with discard
3152 // failing compilation sometimes
3153 if (!EmitFunctionBodyWithDiscard(func)) {
3154 return false;
3155 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003156 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003157 if (!EmitStatementsWithIndent(func->body->statements)) {
3158 return false;
3159 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003160 }
3161
dan sinclair67a18932023-06-26 19:54:49 +00003162 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003163
dan sinclair41e4d9a2022-05-01 14:40:55 +00003164 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003165}
3166
dan sinclair0bfeaf92023-07-26 17:39:51 +00003167bool ASTPrinter::EmitFunctionBodyWithDiscard(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003168 // FXC sometimes fails to compile functions that discard with 'Not all control
3169 // paths return a value'. We work around this by wrapping the function body
3170 // within an "if (true) { <body> } return <default return type obj>;" so that
3171 // there is always an (unused) return statement.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003172
dan sinclair41e4d9a2022-05-01 14:40:55 +00003173 auto* sem = builder_.Sem().Get(func);
Ben Claytonf848af22023-07-28 16:37:32 +00003174 TINT_ASSERT(sem->DiscardStatement() && !sem->ReturnType()->Is<type::Void>());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003175
dan sinclair41e4d9a2022-05-01 14:40:55 +00003176 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003177 Line() << "if (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003178
dan sinclair41e4d9a2022-05-01 14:40:55 +00003179 if (!EmitStatementsWithIndent(func->body->statements)) {
3180 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003181 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003182
dan sinclair67a18932023-06-26 19:54:49 +00003183 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003184
3185 // Return an unused result that matches the type of the return value
dan sinclaird026e132023-04-18 19:38:25 +00003186 auto name = builder_.Symbols().New("unused").Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003187 {
dan sinclair67a18932023-06-26 19:54:49 +00003188 auto out = Line();
dan sinclair2a651632023-02-19 04:03:55 +00003189 if (!EmitTypeAndName(out, sem->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003190 builtin::Access::kReadWrite, name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003191 return false;
3192 }
3193 out << ";";
3194 }
dan sinclair67a18932023-06-26 19:54:49 +00003195 Line() << "return " << name << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003196
3197 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003198}
3199
dan sinclair0bfeaf92023-07-26 17:39:51 +00003200bool ASTPrinter::EmitGlobalVariable(const ast::Variable* global) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003201 return Switch(
3202 global, //
3203 [&](const ast::Var* var) {
3204 auto* sem = builder_.Sem().Get(global);
dan sinclairff7cf212022-10-03 14:05:23 +00003205 switch (sem->AddressSpace()) {
dan sinclair2a651632023-02-19 04:03:55 +00003206 case builtin::AddressSpace::kUniform:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003207 return EmitUniformVariable(var, sem);
dan sinclair2a651632023-02-19 04:03:55 +00003208 case builtin::AddressSpace::kStorage:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003209 return EmitStorageVariable(var, sem);
dan sinclair2a651632023-02-19 04:03:55 +00003210 case builtin::AddressSpace::kHandle:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003211 return EmitHandleVariable(var, sem);
dan sinclair2a651632023-02-19 04:03:55 +00003212 case builtin::AddressSpace::kPrivate:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003213 return EmitPrivateVariable(sem);
dan sinclair2a651632023-02-19 04:03:55 +00003214 case builtin::AddressSpace::kWorkgroup:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003215 return EmitWorkgroupVariable(sem);
dan sinclair2a651632023-02-19 04:03:55 +00003216 case builtin::AddressSpace::kPushConstant:
dan sinclair4abf28e2022-08-02 15:55:35 +00003217 diagnostics_.add_error(
3218 diag::System::Writer,
dan sinclairbae54e72023-07-28 15:01:54 +00003219 "unhandled address space " + tint::ToString(sem->AddressSpace()));
dan sinclair4abf28e2022-08-02 15:55:35 +00003220 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003221 default: {
Ben Claytonf848af22023-07-28 16:37:32 +00003222 TINT_ICE() << "unhandled address space " << sem->AddressSpace();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003223 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003224 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00003225 }
3226 },
dan sinclairf6a94042022-09-09 16:16:19 +00003227 [&](const ast::Override*) {
3228 // Override is removed with SubstituteOverride
Ben Clayton490d9882022-09-21 21:05:45 +00003229 diagnostics_.add_error(diag::System::Writer,
Ben Claytonf10a5792022-10-13 13:47:39 +00003230 "override-expressions should have been removed with the "
Ben Clayton490d9882022-09-21 21:05:45 +00003231 "SubstituteOverride transform");
dan sinclairf6a94042022-09-09 16:16:19 +00003232 return false;
3233 },
Ben Clayton19576e92022-06-28 12:44:16 +00003234 [&](const ast::Const*) {
3235 return true; // Constants are embedded at their use
3236 },
Ben Claytondcdf66e2022-06-17 12:48:51 +00003237 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +00003238 TINT_ICE() << "unhandled global variable type " << global->TypeInfo().name;
dan sinclair4abf28e2022-08-02 15:55:35 +00003239
Ben Claytondcdf66e2022-06-17 12:48:51 +00003240 return false;
3241 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003242}
3243
dan sinclair0bfeaf92023-07-26 17:39:51 +00003244bool ASTPrinter::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Clayton5f4847c2023-04-19 13:24:27 +00003245 auto binding_point = *sem->As<sem::GlobalVariable>()->BindingPoint();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003246 auto* type = sem->Type()->UnwrapRef();
dan sinclaird026e132023-04-18 19:38:25 +00003247 auto name = var->name->symbol.Name();
dan sinclair67a18932023-06-26 19:54:49 +00003248 Line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003249
dan sinclair41e4d9a2022-05-01 14:40:55 +00003250 {
3251 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003252 auto out = Line();
dan sinclair2a651632023-02-19 04:03:55 +00003253 if (!EmitTypeAndName(out, type, builtin::AddressSpace::kUniform, sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003254 return false;
3255 }
3256 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003257 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003258
dan sinclair67a18932023-06-26 19:54:49 +00003259 Line() << "};";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003260
dan sinclair41e4d9a2022-05-01 14:40:55 +00003261 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003262}
3263
dan sinclair0bfeaf92023-07-26 17:39:51 +00003264bool ASTPrinter::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003265 auto* type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003266 auto out = Line();
dan sinclair2a651632023-02-19 04:03:55 +00003267 if (!EmitTypeAndName(out, type, builtin::AddressSpace::kStorage, sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003268 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003269 return false;
3270 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003271
dan sinclairacdf6e12022-08-24 15:47:25 +00003272 auto* global_sem = sem->As<sem::GlobalVariable>();
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003273 out << RegisterAndSpace(sem->Access() == builtin::Access::kRead ? 't' : 'u',
Ben Clayton5f4847c2023-04-19 13:24:27 +00003274 *global_sem->BindingPoint())
dan sinclair41e4d9a2022-05-01 14:40:55 +00003275 << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003276
dan sinclair41e4d9a2022-05-01 14:40:55 +00003277 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003278}
3279
dan sinclair0bfeaf92023-07-26 17:39:51 +00003280bool ASTPrinter::EmitHandleVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003281 auto* unwrapped_type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003282 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003283
dan sinclaird026e132023-04-18 19:38:25 +00003284 auto name = var->name->symbol.Name();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003285 auto* type = sem->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003286 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003287 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003288 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003289
dan sinclair41e4d9a2022-05-01 14:40:55 +00003290 const char* register_space = nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003291
dan sinclair4595fb72022-12-08 14:14:10 +00003292 if (unwrapped_type->Is<type::Texture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003293 register_space = "t";
dan sinclair4595fb72022-12-08 14:14:10 +00003294 if (unwrapped_type->Is<type::StorageTexture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003295 register_space = "u";
3296 }
dan sinclair5ee58b62022-12-08 15:25:18 +00003297 } else if (unwrapped_type->Is<type::Sampler>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003298 register_space = "s";
3299 }
3300
3301 if (register_space) {
dan sinclairacdf6e12022-08-24 15:47:25 +00003302 auto bp = sem->As<sem::GlobalVariable>()->BindingPoint();
Ben Clayton5f4847c2023-04-19 13:24:27 +00003303 out << " : register(" << register_space << bp->binding;
Peng Huangc00ff7f2023-03-31 17:55:19 +00003304 // Omit the space if it's 0, as it's the default.
3305 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better
3306 // compatibility.
Ben Clayton5f4847c2023-04-19 13:24:27 +00003307 if (bp->group == 0) {
Peng Huangc00ff7f2023-03-31 17:55:19 +00003308 out << ")";
3309 } else {
Ben Clayton5f4847c2023-04-19 13:24:27 +00003310 out << ", space" << bp->group << ")";
Peng Huangc00ff7f2023-03-31 17:55:19 +00003311 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003312 }
3313
3314 out << ";";
3315 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003316}
3317
dan sinclair0bfeaf92023-07-26 17:39:51 +00003318bool ASTPrinter::EmitPrivateVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003319 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003320 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003321
dan sinclair41e4d9a2022-05-01 14:40:55 +00003322 out << "static ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003323
dan sinclaird026e132023-04-18 19:38:25 +00003324 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003325 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003326 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003327 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003328 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003329
dan sinclair41e4d9a2022-05-01 14:40:55 +00003330 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003331 if (auto* initializer = decl->initializer) {
3332 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003333 return false;
3334 }
3335 } else {
3336 if (!EmitZeroValue(out, var->Type()->UnwrapRef())) {
3337 return false;
3338 }
3339 }
3340
3341 out << ";";
3342 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003343}
3344
dan sinclair0bfeaf92023-07-26 17:39:51 +00003345bool ASTPrinter::EmitWorkgroupVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003346 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003347 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003348
dan sinclair41e4d9a2022-05-01 14:40:55 +00003349 out << "groupshared ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003350
dan sinclaird026e132023-04-18 19:38:25 +00003351 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003352 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003353 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003354 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003355 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003356
dan sinclair6e77b472022-10-20 13:38:28 +00003357 if (auto* initializer = decl->initializer) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003358 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003359 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003360 return false;
3361 }
3362 }
3363
3364 out << ";";
3365 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003366}
3367
dan sinclair0bfeaf92023-07-26 17:39:51 +00003368std::string ASTPrinter::builtin_to_attribute(builtin::BuiltinValue builtin) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003369 switch (builtin) {
dan sinclair63925792023-02-17 21:56:35 +00003370 case builtin::BuiltinValue::kPosition:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003371 return "SV_Position";
dan sinclair63925792023-02-17 21:56:35 +00003372 case builtin::BuiltinValue::kVertexIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003373 return "SV_VertexID";
dan sinclair63925792023-02-17 21:56:35 +00003374 case builtin::BuiltinValue::kInstanceIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003375 return "SV_InstanceID";
dan sinclair63925792023-02-17 21:56:35 +00003376 case builtin::BuiltinValue::kFrontFacing:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003377 return "SV_IsFrontFace";
dan sinclair63925792023-02-17 21:56:35 +00003378 case builtin::BuiltinValue::kFragDepth:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003379 return "SV_Depth";
dan sinclair63925792023-02-17 21:56:35 +00003380 case builtin::BuiltinValue::kLocalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003381 return "SV_GroupThreadID";
dan sinclair63925792023-02-17 21:56:35 +00003382 case builtin::BuiltinValue::kLocalInvocationIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003383 return "SV_GroupIndex";
dan sinclair63925792023-02-17 21:56:35 +00003384 case builtin::BuiltinValue::kGlobalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003385 return "SV_DispatchThreadID";
dan sinclair63925792023-02-17 21:56:35 +00003386 case builtin::BuiltinValue::kWorkgroupId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003387 return "SV_GroupID";
dan sinclair63925792023-02-17 21:56:35 +00003388 case builtin::BuiltinValue::kSampleIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003389 return "SV_SampleIndex";
dan sinclair63925792023-02-17 21:56:35 +00003390 case builtin::BuiltinValue::kSampleMask:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003391 return "SV_Coverage";
3392 default:
3393 break;
3394 }
3395 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003396}
3397
dan sinclair0bfeaf92023-07-26 17:39:51 +00003398std::string ASTPrinter::interpolation_to_modifiers(builtin::InterpolationType type,
3399 builtin::InterpolationSampling sampling) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003400 std::string modifiers;
3401 switch (type) {
dan sinclair993a6582023-02-20 08:37:45 +00003402 case builtin::InterpolationType::kPerspective:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003403 modifiers += "linear ";
3404 break;
dan sinclair993a6582023-02-20 08:37:45 +00003405 case builtin::InterpolationType::kLinear:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003406 modifiers += "noperspective ";
3407 break;
dan sinclair993a6582023-02-20 08:37:45 +00003408 case builtin::InterpolationType::kFlat:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003409 modifiers += "nointerpolation ";
3410 break;
dan sinclair993a6582023-02-20 08:37:45 +00003411 case builtin::InterpolationType::kUndefined:
Ben Claytonf9ed9d32022-10-11 19:49:17 +00003412 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003413 }
3414 switch (sampling) {
dan sinclair993a6582023-02-20 08:37:45 +00003415 case builtin::InterpolationSampling::kCentroid:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003416 modifiers += "centroid ";
3417 break;
dan sinclair993a6582023-02-20 08:37:45 +00003418 case builtin::InterpolationSampling::kSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003419 modifiers += "sample ";
3420 break;
dan sinclair993a6582023-02-20 08:37:45 +00003421 case builtin::InterpolationSampling::kCenter:
3422 case builtin::InterpolationSampling::kUndefined:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003423 break;
3424 }
3425 return modifiers;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003426}
3427
dan sinclair0bfeaf92023-07-26 17:39:51 +00003428bool ASTPrinter::EmitEntryPointFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003429 auto* func_sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003430
dan sinclair41e4d9a2022-05-01 14:40:55 +00003431 {
dan sinclair67a18932023-06-26 19:54:49 +00003432 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003433 if (func->PipelineStage() == ast::PipelineStage::kCompute) {
3434 // Emit the workgroup_size attribute.
3435 auto wgsize = func_sem->WorkgroupSize();
3436 out << "[numthreads(";
dan sinclair3a2a2792022-06-29 14:38:15 +00003437 for (size_t i = 0; i < 3; i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003438 if (i > 0) {
3439 out << ", ";
3440 }
Ben Clayton490d9882022-09-21 21:05:45 +00003441 if (!wgsize[i].has_value()) {
3442 diagnostics_.add_error(
3443 diag::System::Writer,
Ben Claytonf10a5792022-10-13 13:47:39 +00003444 "override-expressions should have been removed with the SubstituteOverride "
Ben Clayton490d9882022-09-21 21:05:45 +00003445 "transform");
3446 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003447 }
Ben Clayton490d9882022-09-21 21:05:45 +00003448 out << std::to_string(wgsize[i].value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00003449 }
3450 out << ")]" << std::endl;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003451 }
3452
dan sinclair2a651632023-02-19 04:03:55 +00003453 if (!EmitTypeAndName(out, func_sem->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclaird026e132023-04-18 19:38:25 +00003454 builtin::Access::kUndefined, func->name->symbol.Name())) {
Ben Clayton19068572023-02-07 21:28:09 +00003455 return false;
3456 }
3457 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003458
3459 bool first = true;
3460
3461 // Emit entry point parameters.
3462 for (auto* var : func->params) {
3463 auto* sem = builder_.Sem().Get(var);
3464 auto* type = sem->Type();
Ben Claytonbc9e4222023-04-27 18:31:44 +00003465 if (TINT_UNLIKELY(!type->Is<type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003466 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
3467 // not run, or a builtin parameter was added after it was run.
Ben Claytonf848af22023-07-28 16:37:32 +00003468 TINT_ICE() << "Unsupported non-struct entry point parameter";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003469 }
3470
3471 if (!first) {
3472 out << ", ";
3473 }
3474 first = false;
3475
dan sinclairff7cf212022-10-03 14:05:23 +00003476 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003477 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003478 return false;
3479 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003480 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003481
3482 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003483 }
3484
dan sinclair41e4d9a2022-05-01 14:40:55 +00003485 {
3486 ScopedIndent si(this);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003487
dan sinclair41e4d9a2022-05-01 14:40:55 +00003488 if (!EmitStatements(func->body->statements)) {
3489 return false;
3490 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003491
dan sinclair41e4d9a2022-05-01 14:40:55 +00003492 if (!Is<ast::ReturnStatement>(func->body->Last())) {
dan sinclair637a2fe2023-07-24 21:11:41 +00003493 ast::ReturnStatement ret(GenerationID(), ast::NodeID{}, Source{});
dan sinclair41e4d9a2022-05-01 14:40:55 +00003494 if (!EmitStatement(&ret)) {
3495 return false;
3496 }
3497 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003498 }
3499
dan sinclair67a18932023-06-26 19:54:49 +00003500 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003501
dan sinclair41e4d9a2022-05-01 14:40:55 +00003502 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003503}
3504
dan sinclairbae54e72023-07-28 15:01:54 +00003505bool ASTPrinter::EmitConstant(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00003506 const constant::Value* constant,
3507 bool is_variable_initializer) {
Ben Clayton50414802022-06-24 08:06:19 +00003508 return Switch(
Ben Claytonaa037ac2022-06-29 19:07:30 +00003509 constant->Type(), //
dan sinclaird37ecf92022-12-08 16:39:59 +00003510 [&](const type::Bool*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003511 out << (constant->ValueAs<AInt>() ? "true" : "false");
Ben Claytone9f8b092022-06-01 13:14:39 +00003512 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003513 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003514 [&](const type::F32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003515 PrintF32(out, constant->ValueAs<f32>());
Ben Clayton50414802022-06-24 08:06:19 +00003516 return true;
3517 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003518 [&](const type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003519 // emit a f16 scalar with explicit float16_t type declaration.
3520 out << "float16_t(";
dan sinclair5addefb2022-12-14 20:46:32 +00003521 PrintF16(out, constant->ValueAs<f16>());
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003522 out << ")";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003523 return true;
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003524 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003525 [&](const type::I32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003526 out << constant->ValueAs<AInt>();
Ben Clayton50414802022-06-24 08:06:19 +00003527 return true;
3528 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003529 [&](const type::U32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003530 out << constant->ValueAs<AInt>() << "u";
Ben Clayton50414802022-06-24 08:06:19 +00003531 return true;
3532 },
dan sinclair0e780da2022-12-08 22:21:24 +00003533 [&](const type::Vector* v) {
James Price7bca4d72023-03-13 19:05:16 +00003534 if (auto* splat = constant->As<constant::Splat>()) {
Ben Clayton50414802022-06-24 08:06:19 +00003535 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00003536 ScopedParen sp(out);
James Price7bca4d72023-03-13 19:05:16 +00003537 if (!EmitConstant(out, splat->el, is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003538 return false;
3539 }
3540 }
3541 out << ".";
Ben Claytonaa037ac2022-06-29 19:07:30 +00003542 for (size_t i = 0; i < v->Width(); i++) {
Ben Clayton50414802022-06-24 08:06:19 +00003543 out << "x";
3544 }
3545 return true;
3546 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003547
dan sinclair2a651632023-02-19 04:03:55 +00003548 if (!EmitType(out, v, builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003549 "")) {
Ben Clayton50414802022-06-24 08:06:19 +00003550 return false;
3551 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003552
dan sinclairb2ba57b2023-02-28 15:14:09 +00003553 ScopedParen sp(out);
Ben Claytone9f8b092022-06-01 13:14:39 +00003554
Ben Claytonaa037ac2022-06-29 19:07:30 +00003555 for (size_t i = 0; i < v->Width(); i++) {
3556 if (i > 0) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003557 out << ", ";
3558 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003559 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003560 return false;
3561 }
3562 }
3563 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003564 },
dan sinclair0e780da2022-12-08 22:21:24 +00003565 [&](const type::Matrix* m) {
dan sinclair2a651632023-02-19 04:03:55 +00003566 if (!EmitType(out, m, builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00003567 "")) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003568 return false;
3569 }
Ben Clayton50414802022-06-24 08:06:19 +00003570
dan sinclairb2ba57b2023-02-28 15:14:09 +00003571 ScopedParen sp(out);
Ben Clayton50414802022-06-24 08:06:19 +00003572
Ben Claytonaa037ac2022-06-29 19:07:30 +00003573 for (size_t i = 0; i < m->columns(); i++) {
3574 if (i > 0) {
Ben Clayton50414802022-06-24 08:06:19 +00003575 out << ", ";
3576 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003577 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003578 return false;
3579 }
3580 }
3581 return true;
3582 },
dan sinclair946858a2022-12-08 22:21:24 +00003583 [&](const type::Array* a) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003584 if (constant->AllZero()) {
Ben Clayton19576e92022-06-28 12:44:16 +00003585 out << "(";
dan sinclair2a651632023-02-19 04:03:55 +00003586 if (!EmitType(out, a, builtin::AddressSpace::kUndefined,
3587 builtin::Access::kUndefined, "")) {
Ben Clayton19576e92022-06-28 12:44:16 +00003588 return false;
3589 }
3590 out << ")0";
3591 return true;
3592 }
3593
3594 out << "{";
3595 TINT_DEFER(out << "}");
3596
dan sinclair78f80672022-09-22 22:28:21 +00003597 auto count = a->ConstantCount();
3598 if (!count) {
dan sinclair946858a2022-12-08 22:21:24 +00003599 diagnostics_.add_error(diag::System::Writer,
3600 type::Array::kErrExpectedConstantCount);
dan sinclair78f80672022-09-22 22:28:21 +00003601 return false;
3602 }
3603
3604 for (size_t i = 0; i < count; i++) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003605 if (i > 0) {
Ben Clayton19576e92022-06-28 12:44:16 +00003606 out << ", ";
3607 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003608 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton19576e92022-06-28 12:44:16 +00003609 return false;
3610 }
3611 }
3612
3613 return true;
3614 },
Ben Claytonbc9e4222023-04-27 18:31:44 +00003615 [&](const type::Struct* s) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003616 if (!EmitStructType(&helpers_, s)) {
3617 return false;
3618 }
3619
Ben Clayton6c098ba2022-07-14 20:46:39 +00003620 if (constant->AllZero()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003621 out << "(" << StructName(s) << ")0";
Ben Clayton6c098ba2022-07-14 20:46:39 +00003622 return true;
3623 }
3624
dan sinclairbae54e72023-07-28 15:01:54 +00003625 auto emit_member_values = [&](StringStream& o) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003626 o << "{";
dan sinclairad9cd0a2022-12-06 20:01:54 +00003627 for (size_t i = 0; i < s->Members().Length(); i++) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003628 if (i > 0) {
3629 o << ", ";
3630 }
3631 if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
3632 return false;
3633 }
Ben Clayton6c098ba2022-07-14 20:46:39 +00003634 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003635 o << "}";
3636 return true;
3637 };
3638
3639 if (is_variable_initializer) {
3640 if (!emit_member_values(out)) {
Ben Clayton6c098ba2022-07-14 20:46:39 +00003641 return false;
3642 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003643 } else {
3644 // HLSL requires structure initializers to be assigned directly to a variable.
3645 auto name = UniqueIdentifier("c");
3646 {
dan sinclair67a18932023-06-26 19:54:49 +00003647 auto decl = Line();
Ben Clayton329dfd72022-11-23 00:05:05 +00003648 decl << "const " << StructName(s) << " " << name << " = ";
3649 if (!emit_member_values(decl)) {
3650 return false;
3651 }
3652 decl << ";";
3653 }
3654 out << name;
Ben Clayton6c098ba2022-07-14 20:46:39 +00003655 }
3656
3657 return true;
3658 },
Ben Claytone9f8b092022-06-01 13:14:39 +00003659 [&](Default) {
Ben Clayton1545ca12023-05-03 16:26:40 +00003660 diagnostics_.add_error(diag::System::Writer,
3661 "unhandled constant type: " + constant->Type()->FriendlyName());
Ben Claytone9f8b092022-06-01 13:14:39 +00003662 return false;
3663 });
3664}
3665
dan sinclairbae54e72023-07-28 15:01:54 +00003666bool ASTPrinter::EmitLiteral(StringStream& out, const ast::LiteralExpression* lit) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003667 return Switch(
3668 lit,
3669 [&](const ast::BoolLiteralExpression* l) {
3670 out << (l->value ? "true" : "false");
3671 return true;
3672 },
Ben Clayton3ad927c2022-05-25 23:12:14 +00003673 [&](const ast::FloatLiteralExpression* l) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003674 if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
3675 // Emit f16 literal with explicit float16_t type declaration.
3676 out << "float16_t(";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003677 PrintF16(out, static_cast<float>(l->value));
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003678 out << ")";
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003679 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003680 PrintF32(out, static_cast<float>(l->value));
dan sinclair41e4d9a2022-05-01 14:40:55 +00003681 return true;
3682 },
Ben Clayton8822e292022-05-04 22:18:49 +00003683 [&](const ast::IntLiteralExpression* i) {
3684 out << i->value;
3685 switch (i->suffix) {
3686 case ast::IntLiteralExpression::Suffix::kNone:
3687 case ast::IntLiteralExpression::Suffix::kI:
3688 return true;
3689 case ast::IntLiteralExpression::Suffix::kU:
3690 out << "u";
3691 return true;
3692 }
3693 diagnostics_.add_error(diag::System::Writer, "unknown integer literal suffix type");
3694 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003695 },
3696 [&](Default) {
3697 diagnostics_.add_error(diag::System::Writer, "unknown literal type");
3698 return false;
3699 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003700}
3701
dan sinclairbae54e72023-07-28 15:01:54 +00003702bool ASTPrinter::EmitValue(StringStream& out, const type::Type* type, int value) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003703 return Switch(
3704 type,
dan sinclaird37ecf92022-12-08 16:39:59 +00003705 [&](const type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003706 out << (value == 0 ? "false" : "true");
3707 return true;
3708 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003709 [&](const type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003710 out << value << ".0f";
3711 return true;
3712 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003713 [&](const type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003714 out << "float16_t(" << value << ".0h)";
3715 return true;
3716 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003717 [&](const type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003718 out << value;
3719 return true;
3720 },
dan sinclaird37ecf92022-12-08 16:39:59 +00003721 [&](const type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003722 out << value << "u";
3723 return true;
3724 },
dan sinclair0e780da2022-12-08 22:21:24 +00003725 [&](const type::Vector* vec) {
dan sinclair2a651632023-02-19 04:03:55 +00003726 if (!EmitType(out, type, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003727 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003728 return false;
3729 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003730 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003731 for (uint32_t i = 0; i < vec->Width(); i++) {
3732 if (i != 0) {
3733 out << ", ";
3734 }
3735 if (!EmitValue(out, vec->type(), value)) {
3736 return false;
3737 }
3738 }
3739 return true;
3740 },
dan sinclair0e780da2022-12-08 22:21:24 +00003741 [&](const type::Matrix* mat) {
dan sinclair2a651632023-02-19 04:03:55 +00003742 if (!EmitType(out, type, builtin::AddressSpace::kUndefined, builtin::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003743 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003744 return false;
3745 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003746 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003747 for (uint32_t i = 0; i < (mat->rows() * mat->columns()); i++) {
3748 if (i != 0) {
3749 out << ", ";
3750 }
3751 if (!EmitValue(out, mat->type(), value)) {
3752 return false;
3753 }
3754 }
3755 return true;
3756 },
Ben Claytonbc9e4222023-04-27 18:31:44 +00003757 [&](const type::Struct*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003758 out << "(";
3759 TINT_DEFER(out << ")" << value);
dan sinclair2a651632023-02-19 04:03:55 +00003760 return EmitType(out, type, builtin::AddressSpace::kUndefined,
3761 builtin::Access::kUndefined, "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003762 },
dan sinclair946858a2022-12-08 22:21:24 +00003763 [&](const type::Array*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003764 out << "(";
3765 TINT_DEFER(out << ")" << value);
dan sinclair2a651632023-02-19 04:03:55 +00003766 return EmitType(out, type, builtin::AddressSpace::kUndefined,
3767 builtin::Access::kUndefined, "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003768 },
3769 [&](Default) {
dan sinclaird026e132023-04-18 19:38:25 +00003770 diagnostics_.add_error(diag::System::Writer,
3771 "Invalid type for value emission: " + type->FriendlyName());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003772 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003773 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003774}
3775
dan sinclairbae54e72023-07-28 15:01:54 +00003776bool ASTPrinter::EmitZeroValue(StringStream& out, const type::Type* type) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003777 return EmitValue(out, type, 0);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003778}
3779
dan sinclair0bfeaf92023-07-26 17:39:51 +00003780bool ASTPrinter::EmitLoop(const ast::LoopStatement* stmt) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00003781 auto emit_continuing = [this, stmt] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003782 if (stmt->continuing && !stmt->continuing->Empty()) {
3783 if (!EmitBlock(stmt->continuing)) {
3784 return false;
3785 }
3786 }
3787 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003788 };
3789
3790 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00003791 Line() << "while (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003792 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003793 ScopedIndent si(this);
3794 if (!EmitStatements(stmt->body->statements)) {
3795 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003796 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003797 if (!emit_continuing_()) {
3798 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003799 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003800 }
dan sinclair67a18932023-06-26 19:54:49 +00003801 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003802
dan sinclair41e4d9a2022-05-01 14:40:55 +00003803 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003804}
3805
dan sinclair0bfeaf92023-07-26 17:39:51 +00003806bool ASTPrinter::EmitForLoop(const ast::ForLoopStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003807 // Nest a for loop with a new block. In HLSL the initializer scope is not
3808 // nested by the for-loop, so we may get variable redefinitions.
dan sinclair67a18932023-06-26 19:54:49 +00003809 Line() << "{";
3810 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003811 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003812 DecrementIndent();
3813 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003814 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003815
dan sinclair41e4d9a2022-05-01 14:40:55 +00003816 TextBuffer init_buf;
3817 if (auto* init = stmt->initializer) {
3818 TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
3819 if (!EmitStatement(init)) {
3820 return false;
3821 }
3822 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003823
dan sinclair41e4d9a2022-05-01 14:40:55 +00003824 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00003825 StringStream cond_buf;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003826 if (auto* cond = stmt->condition) {
3827 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
3828 if (!EmitExpression(cond_buf, cond)) {
3829 return false;
3830 }
3831 }
3832
3833 TextBuffer cont_buf;
3834 if (auto* cont = stmt->continuing) {
3835 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
3836 if (!EmitStatement(cont)) {
3837 return false;
3838 }
3839 }
3840
3841 // If the for-loop has a multi-statement conditional and / or continuing, then
3842 // we cannot emit this as a regular for-loop in HLSL. Instead we need to
3843 // generate a `while(true)` loop.
3844 bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
3845
3846 // If the for-loop has multi-statement initializer, or is going to be emitted
3847 // as a `while(true)` loop, then declare the initializer statement(s) before
3848 // the loop.
3849 if (init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop)) {
3850 current_buffer_->Append(init_buf);
3851 init_buf.lines.clear(); // Don't emit the initializer again in the 'for'
3852 }
3853
3854 if (emit_as_loop) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00003855 auto emit_continuing = [&] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003856 current_buffer_->Append(cont_buf);
3857 return true;
3858 };
3859
3860 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00003861 Line() << "while (true) {";
3862 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003863 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003864 DecrementIndent();
3865 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003866 });
3867
3868 if (stmt->condition) {
3869 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00003870 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003871 }
3872
3873 if (!EmitStatements(stmt->body->statements)) {
3874 return false;
3875 }
3876
3877 if (!emit_continuing_()) {
3878 return false;
3879 }
3880 } else {
3881 // For-loop can be generated.
3882 {
dan sinclair67a18932023-06-26 19:54:49 +00003883 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00003884 out << "for";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003885 {
3886 ScopedParen sp(out);
3887
3888 if (!init_buf.lines.empty()) {
3889 out << init_buf.lines[0].content << " ";
3890 } else {
3891 out << "; ";
3892 }
3893
3894 out << cond_buf.str() << "; ";
3895
3896 if (!cont_buf.lines.empty()) {
dan sinclairbae54e72023-07-28 15:01:54 +00003897 out << tint::TrimSuffix(cont_buf.lines[0].content, ";");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003898 }
3899 }
3900 out << " {";
3901 }
3902 {
3903 auto emit_continuing = [] { return true; };
3904 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
3905 if (!EmitStatementsWithIndent(stmt->body->statements)) {
3906 return false;
3907 }
3908 }
dan sinclair67a18932023-06-26 19:54:49 +00003909 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003910 }
3911
3912 return true;
3913}
3914
dan sinclair0bfeaf92023-07-26 17:39:51 +00003915bool ASTPrinter::EmitWhile(const ast::WhileStatement* stmt) {
dan sinclair49d1a2d2022-06-16 12:01:27 +00003916 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00003917 StringStream cond_buf;
dan sinclair49d1a2d2022-06-16 12:01:27 +00003918 {
3919 auto* cond = stmt->condition;
3920 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
3921 if (!EmitExpression(cond_buf, cond)) {
3922 return false;
3923 }
3924 }
3925
Ben Claytona0aabaf2023-06-22 23:53:09 +00003926 auto emit_continuing = [&] { return true; };
dan sinclair4b88dbc2022-06-16 15:27:38 +00003927 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
3928
dan sinclair49d1a2d2022-06-16 12:01:27 +00003929 // If the while has a multi-statement conditional, then we cannot emit this
3930 // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
3931 bool emit_as_loop = cond_pre.lines.size() > 0;
3932 if (emit_as_loop) {
dan sinclair67a18932023-06-26 19:54:49 +00003933 Line() << "while (true) {";
3934 IncrementIndent();
dan sinclair49d1a2d2022-06-16 12:01:27 +00003935 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003936 DecrementIndent();
3937 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00003938 });
3939
3940 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00003941 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair49d1a2d2022-06-16 12:01:27 +00003942 if (!EmitStatements(stmt->body->statements)) {
3943 return false;
3944 }
3945 } else {
3946 // While can be generated.
3947 {
dan sinclair67a18932023-06-26 19:54:49 +00003948 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00003949 out << "while";
dan sinclair49d1a2d2022-06-16 12:01:27 +00003950 {
3951 ScopedParen sp(out);
3952 out << cond_buf.str();
3953 }
3954 out << " {";
3955 }
3956 if (!EmitStatementsWithIndent(stmt->body->statements)) {
3957 return false;
3958 }
dan sinclair67a18932023-06-26 19:54:49 +00003959 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00003960 }
3961
3962 return true;
3963}
3964
dan sinclairbae54e72023-07-28 15:01:54 +00003965bool ASTPrinter::EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr) {
Ben Claytonad315652023-02-05 12:36:50 +00003966 if (!EmitExpression(out, expr->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003967 return false;
3968 }
3969 out << ".";
3970
Ben Clayton2f9a9882022-12-17 02:20:04 +00003971 auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003972
Ben Clayton10fae7a2022-11-14 15:29:29 +00003973 return Switch(
3974 sem,
3975 [&](const sem::Swizzle*) {
3976 // Swizzles output the name directly
dan sinclaird026e132023-04-18 19:38:25 +00003977 out << expr->member->symbol.Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00003978 return true;
3979 },
3980 [&](const sem::StructMemberAccess* member_access) {
dan sinclaird026e132023-04-18 19:38:25 +00003981 out << member_access->Member()->Name().Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00003982 return true;
3983 },
3984 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +00003985 TINT_ICE() << "unknown member access type: " << sem->TypeInfo().name;
Ben Clayton10fae7a2022-11-14 15:29:29 +00003986 return false;
3987 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003988}
3989
dan sinclair0bfeaf92023-07-26 17:39:51 +00003990bool ASTPrinter::EmitReturn(const ast::ReturnStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003991 if (stmt->value) {
dan sinclair67a18932023-06-26 19:54:49 +00003992 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003993 out << "return ";
3994 if (!EmitExpression(out, stmt->value)) {
3995 return false;
3996 }
3997 out << ";";
3998 } else {
dan sinclair67a18932023-06-26 19:54:49 +00003999 Line() << "return;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004000 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004001 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004002}
4003
dan sinclair0bfeaf92023-07-26 17:39:51 +00004004bool ASTPrinter::EmitStatement(const ast::Statement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004005 return Switch(
4006 stmt,
4007 [&](const ast::AssignmentStatement* a) { //
4008 return EmitAssign(a);
4009 },
4010 [&](const ast::BlockStatement* b) { //
4011 return EmitBlock(b);
4012 },
4013 [&](const ast::BreakStatement* b) { //
4014 return EmitBreak(b);
4015 },
dan sinclairb8b0c212022-10-20 22:45:50 +00004016 [&](const ast::BreakIfStatement* b) { //
4017 return EmitBreakIf(b);
4018 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004019 [&](const ast::CallStatement* c) { //
dan sinclair67a18932023-06-26 19:54:49 +00004020 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004021 if (!EmitCall(out, c->expr)) {
4022 return false;
4023 }
4024 out << ";";
4025 return true;
4026 },
4027 [&](const ast::ContinueStatement* c) { //
4028 return EmitContinue(c);
4029 },
4030 [&](const ast::DiscardStatement* d) { //
4031 return EmitDiscard(d);
4032 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004033 [&](const ast::IfStatement* i) { //
4034 return EmitIf(i);
4035 },
4036 [&](const ast::LoopStatement* l) { //
4037 return EmitLoop(l);
4038 },
4039 [&](const ast::ForLoopStatement* l) { //
4040 return EmitForLoop(l);
4041 },
dan sinclair49d1a2d2022-06-16 12:01:27 +00004042 [&](const ast::WhileStatement* l) { //
4043 return EmitWhile(l);
4044 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004045 [&](const ast::ReturnStatement* r) { //
4046 return EmitReturn(r);
4047 },
4048 [&](const ast::SwitchStatement* s) { //
4049 return EmitSwitch(s);
4050 },
4051 [&](const ast::VariableDeclStatement* v) { //
Ben Claytondcdf66e2022-06-17 12:48:51 +00004052 return Switch(
4053 v->variable, //
4054 [&](const ast::Var* var) { return EmitVar(var); },
4055 [&](const ast::Let* let) { return EmitLet(let); },
Ben Clayton19576e92022-06-28 12:44:16 +00004056 [&](const ast::Const*) {
4057 return true; // Constants are embedded at their use
4058 },
Ben Claytondcdf66e2022-06-17 12:48:51 +00004059 [&](Default) { //
Ben Claytonf848af22023-07-28 16:37:32 +00004060 TINT_ICE() << "unknown variable type: " << v->variable->TypeInfo().name;
Ben Claytondcdf66e2022-06-17 12:48:51 +00004061 return false;
4062 });
dan sinclair41e4d9a2022-05-01 14:40:55 +00004063 },
Ben Claytonc98d57d2023-01-24 14:59:43 +00004064 [&](const ast::ConstAssert*) {
Ben Claytonb4744ac2022-08-03 07:01:08 +00004065 return true; // Not emitted
4066 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004067 [&](Default) { //
4068 diagnostics_.add_error(diag::System::Writer,
4069 "unknown statement type: " + std::string(stmt->TypeInfo().name));
4070 return false;
4071 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004072}
4073
dan sinclair0bfeaf92023-07-26 17:39:51 +00004074bool ASTPrinter::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
Ben Claytonf848af22023-07-28 16:37:32 +00004075 TINT_ASSERT(stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004076
dan sinclair41e4d9a2022-05-01 14:40:55 +00004077 // FXC fails to compile a switch with just a default case, ignoring the
4078 // default case body. We work around this here by emitting the default case
4079 // without the switch.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004080
Antonio Maioranoeab1f622023-02-01 15:46:34 +00004081 // Emit the switch condition as-is if it has side-effects (e.g.
4082 // function call). Note that we can ignore the result of the expression (if any).
Ben Clayton0b4a2f12023-02-05 22:59:40 +00004083 if (auto* sem_cond = builder_.Sem().GetVal(stmt->condition); sem_cond->HasSideEffects()) {
dan sinclair67a18932023-06-26 19:54:49 +00004084 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004085 if (!EmitExpression(out, stmt->condition)) {
4086 return false;
4087 }
4088 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004089 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004090
dan sinclair41e4d9a2022-05-01 14:40:55 +00004091 // Emit "do { <default case body> } while(false);". We use a 'do' loop so
4092 // that break statements work as expected, and make it 'while (false)' in
4093 // case there isn't a break statement.
dan sinclair67a18932023-06-26 19:54:49 +00004094 Line() << "do {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004095 {
4096 ScopedIndent si(this);
4097 if (!EmitStatements(stmt->body[0]->body->statements)) {
4098 return false;
4099 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004100 }
dan sinclair67a18932023-06-26 19:54:49 +00004101 Line() << "} while (false);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004102 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004103}
4104
dan sinclair0bfeaf92023-07-26 17:39:51 +00004105bool ASTPrinter::EmitSwitch(const ast::SwitchStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004106 // BUG(crbug.com/tint/1188): work around default-only switches
dan sinclairf148f082022-10-19 15:55:02 +00004107 if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
4108 stmt->body[0]->ContainsDefault()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004109 return EmitDefaultOnlySwitch(stmt);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004110 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004111
dan sinclair41e4d9a2022-05-01 14:40:55 +00004112 { // switch(expr) {
dan sinclair67a18932023-06-26 19:54:49 +00004113 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004114 out << "switch(";
4115 if (!EmitExpression(out, stmt->condition)) {
4116 return false;
4117 }
4118 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004119 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004120
dan sinclair41e4d9a2022-05-01 14:40:55 +00004121 {
4122 ScopedIndent si(this);
Ben Clayton783b1692022-08-02 17:03:35 +00004123 for (size_t i = 0; i < stmt->body.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004124 if (!EmitCase(stmt, i)) {
4125 return false;
4126 }
4127 }
4128 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004129
dan sinclair67a18932023-06-26 19:54:49 +00004130 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004131
4132 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004133}
4134
dan sinclairbae54e72023-07-28 15:01:54 +00004135bool ASTPrinter::EmitType(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004136 const type::Type* type,
4137 builtin::AddressSpace address_space,
4138 builtin::Access access,
4139 const std::string& name,
4140 bool* name_printed /* = nullptr */) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004141 if (name_printed) {
4142 *name_printed = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004143 }
dan sinclairff7cf212022-10-03 14:05:23 +00004144 switch (address_space) {
dan sinclair2a651632023-02-19 04:03:55 +00004145 case builtin::AddressSpace::kStorage:
dan sinclairb6cc4cb2023-02-19 04:01:29 +00004146 if (access != builtin::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004147 out << "RW";
4148 }
4149 out << "ByteAddressBuffer";
4150 return true;
dan sinclair2a651632023-02-19 04:03:55 +00004151 case builtin::AddressSpace::kUniform: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004152 auto array_length = (type->Size() + 15) / 16;
4153 out << "uint4 " << name << "[" << array_length << "]";
4154 if (name_printed) {
4155 *name_printed = true;
4156 }
4157 return true;
4158 }
4159 default:
4160 break;
4161 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004162
dan sinclair41e4d9a2022-05-01 14:40:55 +00004163 return Switch(
4164 type,
dan sinclair946858a2022-12-08 22:21:24 +00004165 [&](const type::Array* ary) {
dan sinclair5f764d82022-12-08 00:32:27 +00004166 const type::Type* base_type = ary;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004167 std::vector<uint32_t> sizes;
dan sinclair946858a2022-12-08 22:21:24 +00004168 while (auto* arr = base_type->As<type::Array>()) {
Ben Clayton884f9522023-01-12 22:52:57 +00004169 if (TINT_UNLIKELY(arr->Count()->Is<type::RuntimeArrayCount>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004170 TINT_ICE()
dan sinclair78f80672022-09-22 22:28:21 +00004171 << "runtime arrays may only exist in storage buffers, which should have "
Ben Clayton3a68ab42022-06-24 08:30:28 +00004172 "been transformed into a ByteAddressBuffer";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004173 return false;
4174 }
dan sinclair78f80672022-09-22 22:28:21 +00004175 const auto count = arr->ConstantCount();
4176 if (!count) {
4177 diagnostics_.add_error(diag::System::Writer,
dan sinclair946858a2022-12-08 22:21:24 +00004178 type::Array::kErrExpectedConstantCount);
dan sinclair78f80672022-09-22 22:28:21 +00004179 return false;
4180 }
4181
4182 sizes.push_back(count.value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004183 base_type = arr->ElemType();
4184 }
dan sinclairff7cf212022-10-03 14:05:23 +00004185 if (!EmitType(out, base_type, address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004186 return false;
4187 }
4188 if (!name.empty()) {
4189 out << " " << name;
4190 if (name_printed) {
4191 *name_printed = true;
4192 }
4193 }
4194 for (uint32_t size : sizes) {
4195 out << "[" << size << "]";
4196 }
4197 return true;
4198 },
dan sinclaird37ecf92022-12-08 16:39:59 +00004199 [&](const type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004200 out << "bool";
4201 return true;
4202 },
dan sinclaird37ecf92022-12-08 16:39:59 +00004203 [&](const type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004204 out << "float";
4205 return true;
4206 },
dan sinclaird37ecf92022-12-08 16:39:59 +00004207 [&](const type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004208 out << "float16_t";
4209 return true;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +00004210 },
dan sinclaird37ecf92022-12-08 16:39:59 +00004211 [&](const type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004212 out << "int";
4213 return true;
4214 },
dan sinclair0e780da2022-12-08 22:21:24 +00004215 [&](const type::Matrix* mat) {
dan sinclaird37ecf92022-12-08 16:39:59 +00004216 if (mat->type()->Is<type::F16>()) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004217 // Use matrix<type, N, M> for f16 matrix
4218 out << "matrix<";
dan sinclairff7cf212022-10-03 14:05:23 +00004219 if (!EmitType(out, mat->type(), address_space, access, "")) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004220 return false;
4221 }
4222 out << ", " << mat->columns() << ", " << mat->rows() << ">";
4223 return true;
4224 }
dan sinclairff7cf212022-10-03 14:05:23 +00004225 if (!EmitType(out, mat->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004226 return false;
4227 }
4228 // Note: HLSL's matrices are declared as <type>NxM, where N is the
4229 // number of rows and M is the number of columns. Despite HLSL's
4230 // matrices being column-major by default, the index operator and
dan sinclair6e77b472022-10-20 13:38:28 +00004231 // initializers actually operate on row-vectors, where as WGSL operates
dan sinclair41e4d9a2022-05-01 14:40:55 +00004232 // on column vectors. To simplify everything we use the transpose of the
4233 // matrices. See:
4234 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
4235 out << mat->columns() << "x" << mat->rows();
4236 return true;
4237 },
dan sinclair4d56b482022-12-08 17:50:50 +00004238 [&](const type::Pointer*) {
Ben Claytonf848af22023-07-28 16:37:32 +00004239 TINT_ICE() << "Attempting to emit pointer type. These should have "
4240 "been removed with the SimplifyPointers transform";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004241 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004242 },
dan sinclair5ee58b62022-12-08 15:25:18 +00004243 [&](const type::Sampler* sampler) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004244 out << "Sampler";
4245 if (sampler->IsComparison()) {
4246 out << "Comparison";
4247 }
4248 out << "State";
4249 return true;
4250 },
Ben Claytonbc9e4222023-04-27 18:31:44 +00004251 [&](const type::Struct* str) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004252 out << StructName(str);
4253 return true;
4254 },
dan sinclair4595fb72022-12-08 14:14:10 +00004255 [&](const type::Texture* tex) {
Ben Clayton884f9522023-01-12 22:52:57 +00004256 if (TINT_UNLIKELY(tex->Is<type::ExternalTexture>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004257 TINT_ICE() << "Multiplanar external texture transform was not run.";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004258 return false;
4259 }
Brandon Jones6661b282022-02-25 20:14:52 +00004260
dan sinclair4595fb72022-12-08 14:14:10 +00004261 auto* storage = tex->As<type::StorageTexture>();
4262 auto* ms = tex->As<type::MultisampledTexture>();
4263 auto* depth_ms = tex->As<type::DepthMultisampledTexture>();
4264 auto* sampled = tex->As<type::SampledTexture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004265
dan sinclairb6cc4cb2023-02-19 04:01:29 +00004266 if (storage && storage->access() != builtin::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004267 out << "RW";
4268 }
4269 out << "Texture";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004270
dan sinclair41e4d9a2022-05-01 14:40:55 +00004271 switch (tex->dim()) {
dan sinclair3cbf3fc2023-01-21 19:16:15 +00004272 case type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004273 out << "1D";
4274 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00004275 case type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004276 out << ((ms || depth_ms) ? "2DMS" : "2D");
4277 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00004278 case type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004279 out << ((ms || depth_ms) ? "2DMSArray" : "2DArray");
4280 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00004281 case type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004282 out << "3D";
4283 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00004284 case type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004285 out << "Cube";
4286 break;
dan sinclair3cbf3fc2023-01-21 19:16:15 +00004287 case type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004288 out << "CubeArray";
4289 break;
4290 default:
Ben Claytonf848af22023-07-28 16:37:32 +00004291 TINT_UNREACHABLE() << "unexpected TextureDimension " << tex->dim();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004292 return false;
4293 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004294
dan sinclair41e4d9a2022-05-01 14:40:55 +00004295 if (storage) {
4296 auto* component = image_format_to_rwtexture_type(storage->texel_format());
Ben Clayton884f9522023-01-12 22:52:57 +00004297 if (TINT_UNLIKELY(!component)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004298 TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
4299 << static_cast<int>(storage->texel_format());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004300 return false;
4301 }
4302 out << "<" << component << ">";
4303 } else if (depth_ms) {
4304 out << "<float4>";
4305 } else if (sampled || ms) {
4306 auto* subtype = sampled ? sampled->type() : ms->type();
4307 out << "<";
dan sinclaird37ecf92022-12-08 16:39:59 +00004308 if (subtype->Is<type::F32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004309 out << "float4";
dan sinclaird37ecf92022-12-08 16:39:59 +00004310 } else if (subtype->Is<type::I32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004311 out << "int4";
Ben Clayton884f9522023-01-12 22:52:57 +00004312 } else if (TINT_LIKELY(subtype->Is<type::U32>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004313 out << "uint4";
4314 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004315 TINT_ICE() << "Unsupported multisampled texture type";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004316 return false;
4317 }
4318 out << ">";
4319 }
4320 return true;
4321 },
dan sinclaird37ecf92022-12-08 16:39:59 +00004322 [&](const type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004323 out << "uint";
4324 return true;
4325 },
dan sinclair0e780da2022-12-08 22:21:24 +00004326 [&](const type::Vector* vec) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004327 auto width = vec->Width();
dan sinclaird37ecf92022-12-08 16:39:59 +00004328 if (vec->type()->Is<type::F32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004329 out << "float" << width;
dan sinclaird37ecf92022-12-08 16:39:59 +00004330 } else if (vec->type()->Is<type::I32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004331 out << "int" << width;
dan sinclaird37ecf92022-12-08 16:39:59 +00004332 } else if (vec->type()->Is<type::U32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004333 out << "uint" << width;
dan sinclaird37ecf92022-12-08 16:39:59 +00004334 } else if (vec->type()->Is<type::Bool>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004335 out << "bool" << width;
4336 } else {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004337 // For example, use "vector<float16_t, N>" for f16 vector.
dan sinclair41e4d9a2022-05-01 14:40:55 +00004338 out << "vector<";
dan sinclairff7cf212022-10-03 14:05:23 +00004339 if (!EmitType(out, vec->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004340 return false;
4341 }
4342 out << ", " << width << ">";
4343 }
4344 return true;
4345 },
dan sinclaird8a08452022-12-08 22:21:24 +00004346 [&](const type::Atomic* atomic) {
dan sinclairff7cf212022-10-03 14:05:23 +00004347 return EmitType(out, atomic->Type(), address_space, access, name);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004348 },
dan sinclaird37ecf92022-12-08 16:39:59 +00004349 [&](const type::Void*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004350 out << "void";
4351 return true;
4352 },
4353 [&](Default) {
4354 diagnostics_.add_error(diag::System::Writer, "unknown type in EmitType");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004355 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004356 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004357}
4358
dan sinclairbae54e72023-07-28 15:01:54 +00004359bool ASTPrinter::EmitTypeAndName(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004360 const type::Type* type,
4361 builtin::AddressSpace address_space,
4362 builtin::Access access,
4363 const std::string& name) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004364 bool name_printed = false;
dan sinclairff7cf212022-10-03 14:05:23 +00004365 if (!EmitType(out, type, address_space, access, name, &name_printed)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004366 return false;
4367 }
4368 if (!name.empty() && !name_printed) {
4369 out << " " << name;
4370 }
4371 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004372}
4373
dan sinclair0bfeaf92023-07-26 17:39:51 +00004374bool ASTPrinter::EmitStructType(TextBuffer* b, const type::Struct* str) {
Ben Clayton329dfd72022-11-23 00:05:05 +00004375 auto it = emitted_structs_.emplace(str);
4376 if (!it.second) {
4377 return true;
4378 }
4379
dan sinclair67a18932023-06-26 19:54:49 +00004380 Line(b) << "struct " << StructName(str) << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004381 {
4382 ScopedIndent si(b);
4383 for (auto* mem : str->Members()) {
dan sinclaird026e132023-04-18 19:38:25 +00004384 auto mem_name = mem->Name().Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004385 auto* ty = mem->Type();
dan sinclair67a18932023-06-26 19:54:49 +00004386 auto out = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004387 std::string pre, post;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004388
Ben Clayton576ba1c2023-04-27 17:58:25 +00004389 auto& attributes = mem->Attributes();
Ben Claytonf0b4dbb2023-02-21 21:05:28 +00004390
Ben Clayton576ba1c2023-04-27 17:58:25 +00004391 if (auto location = attributes.location) {
4392 auto& pipeline_stage_uses = str->PipelineStageUses();
4393 if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004394 TINT_ICE() << "invalid entry point IO struct uses";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004395 }
Ben Clayton576ba1c2023-04-27 17:58:25 +00004396 if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexInput)) {
4397 post += " : TEXCOORD" + std::to_string(location.value());
4398 } else if (pipeline_stage_uses.count(type::PipelineStageUsage::kVertexOutput)) {
4399 post += " : TEXCOORD" + std::to_string(location.value());
4400 } else if (pipeline_stage_uses.count(type::PipelineStageUsage::kFragmentInput)) {
4401 post += " : TEXCOORD" + std::to_string(location.value());
4402 } else if (TINT_LIKELY(pipeline_stage_uses.count(
4403 type::PipelineStageUsage::kFragmentOutput))) {
Brandon Jones07500d32023-07-21 22:07:03 +00004404 if (auto index = attributes.index) {
4405 post += " : SV_Target" + std::to_string(location.value() + index.value());
4406 } else {
4407 post += " : SV_Target" + std::to_string(location.value());
4408 }
4409
Ben Clayton576ba1c2023-04-27 17:58:25 +00004410 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004411 TINT_ICE() << "invalid use of location attribute";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004412 }
4413 }
4414 if (auto builtin = attributes.builtin) {
4415 auto name = builtin_to_attribute(builtin.value());
4416 if (name.empty()) {
4417 diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
4418 return false;
4419 }
4420 post += " : " + name;
4421 }
4422 if (auto interpolation = attributes.interpolation) {
4423 auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
4424 if (mod.empty()) {
4425 diagnostics_.add_error(diag::System::Writer, "unsupported interpolation");
4426 return false;
4427 }
4428 pre += mod;
4429 }
4430 if (attributes.invariant) {
4431 // Note: `precise` is not exactly the same as `invariant`, but is
4432 // stricter and therefore provides the necessary guarantees.
4433 // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
4434 pre += "precise ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004435 }
4436
dan sinclair41e4d9a2022-05-01 14:40:55 +00004437 out << pre;
dan sinclair2a651632023-02-19 04:03:55 +00004438 if (!EmitTypeAndName(out, ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00004439 builtin::Access::kReadWrite, mem_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004440 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004441 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004442 out << post << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004443 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004444 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004445
dan sinclair67a18932023-06-26 19:54:49 +00004446 Line(b) << "};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004447 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004448}
4449
dan sinclairbae54e72023-07-28 15:01:54 +00004450bool ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004451 switch (expr->op) {
4452 case ast::UnaryOp::kIndirection:
4453 case ast::UnaryOp::kAddressOf:
4454 return EmitExpression(out, expr->expr);
4455 case ast::UnaryOp::kComplement:
4456 out << "~";
4457 break;
4458 case ast::UnaryOp::kNot:
4459 out << "!";
4460 break;
4461 case ast::UnaryOp::kNegation:
4462 out << "-";
4463 break;
4464 }
4465 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004466
dan sinclair41e4d9a2022-05-01 14:40:55 +00004467 if (!EmitExpression(out, expr->expr)) {
4468 return false;
4469 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004470
dan sinclair41e4d9a2022-05-01 14:40:55 +00004471 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004472
dan sinclair41e4d9a2022-05-01 14:40:55 +00004473 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004474}
4475
dan sinclair0bfeaf92023-07-26 17:39:51 +00004476bool ASTPrinter::EmitVar(const ast::Var* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004477 auto* sem = builder_.Sem().Get(var);
4478 auto* type = sem->Type()->UnwrapRef();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004479
dan sinclair67a18932023-06-26 19:54:49 +00004480 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00004481 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004482 return false;
4483 }
4484
4485 out << " = ";
4486
dan sinclair6e77b472022-10-20 13:38:28 +00004487 if (var->initializer) {
4488 if (!EmitExpression(out, var->initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004489 return false;
4490 }
4491 } else {
4492 if (!EmitZeroValue(out, type)) {
4493 return false;
4494 }
4495 }
4496 out << ";";
4497
4498 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004499}
4500
dan sinclair0bfeaf92023-07-26 17:39:51 +00004501bool ASTPrinter::EmitLet(const ast::Let* let) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004502 auto* sem = builder_.Sem().Get(let);
4503 auto* type = sem->Type()->UnwrapRef();
4504
dan sinclair67a18932023-06-26 19:54:49 +00004505 auto out = Line();
Ben Claytondcdf66e2022-06-17 12:48:51 +00004506 out << "const ";
dan sinclair2a651632023-02-19 04:03:55 +00004507 if (!EmitTypeAndName(out, type, builtin::AddressSpace::kUndefined, builtin::Access::kUndefined,
dan sinclaird026e132023-04-18 19:38:25 +00004508 let->name->symbol.Name())) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004509 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004510 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00004511 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00004512 if (!EmitExpression(out, let->initializer)) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004513 return false;
4514 }
4515 out << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004516
Ben Claytondcdf66e2022-06-17 12:48:51 +00004517 return true;
4518}
4519
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004520template <typename F>
dan sinclairbae54e72023-07-28 15:01:54 +00004521bool ASTPrinter::CallBuiltinHelper(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004522 const ast::CallExpression* call,
4523 const sem::Builtin* builtin,
4524 F&& build) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004525 // Generate the helper function if it hasn't been created already
dan sinclairbae54e72023-07-28 15:01:54 +00004526 auto fn = tint::GetOrCreate(builtins_, builtin, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004527 TextBuffer b;
4528 TINT_DEFER(helpers_.Append(b));
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004529
dan sinclair9543f742023-03-09 01:20:16 +00004530 auto fn_name = UniqueIdentifier(std::string("tint_") + builtin::str(builtin->Type()));
dan sinclair41e4d9a2022-05-01 14:40:55 +00004531 std::vector<std::string> parameter_names;
4532 {
dan sinclair67a18932023-06-26 19:54:49 +00004533 auto decl = Line(&b);
dan sinclair2a651632023-02-19 04:03:55 +00004534 if (!EmitTypeAndName(decl, builtin->ReturnType(), builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00004535 builtin::Access::kUndefined, fn_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004536 return "";
4537 }
4538 {
4539 ScopedParen sp(decl);
4540 for (auto* param : builtin->Parameters()) {
4541 if (!parameter_names.empty()) {
4542 decl << ", ";
4543 }
4544 auto param_name = "param_" + std::to_string(parameter_names.size());
4545 const auto* ty = param->Type();
dan sinclair4d56b482022-12-08 17:50:50 +00004546 if (auto* ptr = ty->As<type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004547 decl << "inout ";
4548 ty = ptr->StoreType();
4549 }
dan sinclair2a651632023-02-19 04:03:55 +00004550 if (!EmitTypeAndName(decl, ty, builtin::AddressSpace::kUndefined,
dan sinclairb6cc4cb2023-02-19 04:01:29 +00004551 builtin::Access::kUndefined, param_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004552 return "";
4553 }
4554 parameter_names.emplace_back(std::move(param_name));
4555 }
4556 }
4557 decl << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004558 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004559 {
4560 ScopedIndent si(&b);
4561 if (!build(&b, parameter_names)) {
4562 return "";
4563 }
4564 }
dan sinclair67a18932023-06-26 19:54:49 +00004565 Line(&b) << "}";
4566 Line(&b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004567 return fn_name;
4568 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004569
dan sinclair41e4d9a2022-05-01 14:40:55 +00004570 if (fn.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004571 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004572 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004573
4574 // Call the helper
4575 out << fn;
4576 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00004577 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004578 bool first = true;
4579 for (auto* arg : call->args) {
4580 if (!first) {
4581 out << ", ";
4582 }
4583 first = false;
4584 if (!EmitExpression(out, arg)) {
4585 return false;
4586 }
4587 }
4588 }
4589 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004590}
4591
dan sinclair0bfeaf92023-07-26 17:39:51 +00004592std::string ASTPrinter::UniqueIdentifier(const std::string& prefix /* = "" */) {
Ben Clayton5ed099a2023-07-25 18:52:03 +00004593 return builder_.Symbols().New(prefix).Name();
4594}
4595
dan sinclair0bfeaf92023-07-26 17:39:51 +00004596} // namespace tint::hlsl::writer