Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 1 | // Copyright 2023 The Dawn & Tint Authors |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 2 | // |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 3 | // Redistribution and use in source and binary forms, with or without |
| 4 | // modification, are permitted provided that the following conditions are met: |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 5 | // |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 6 | // 1. Redistributions of source code must retain the above copyright notice, this |
| 7 | // list of conditions and the following disclaimer. |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 8 | // |
Austin Eng | cc2516a | 2023-10-17 20:57:54 +0000 | [diff] [blame] | 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, |
| 10 | // this list of conditions and the following disclaimer in the documentation |
| 11 | // and/or other materials provided with the distribution. |
| 12 | // |
| 13 | // 3. Neither the name of the copyright holder nor the names of its |
| 14 | // contributors may be used to endorse or promote products derived from |
| 15 | // this software without specific prior written permission. |
| 16 | // |
| 17 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 18 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 19 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 20 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| 21 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 22 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 23 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| 24 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 27 | |
dan sinclair | e7e4afc | 2023-07-26 02:28:30 +0000 | [diff] [blame] | 28 | #include "src/tint/lang/msl/writer/printer/printer.h" |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 29 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 30 | #include <unordered_map> |
| 31 | #include <unordered_set> |
dan sinclair | 155a42a | 2023-08-02 20:23:58 +0000 | [diff] [blame] | 32 | #include <utility> |
| 33 | |
dan sinclair | 352f8c8 | 2023-07-21 00:40:07 +0000 | [diff] [blame] | 34 | #include "src/tint/lang/core/constant/composite.h" |
| 35 | #include "src/tint/lang/core/constant/splat.h" |
dan sinclair | ce6dffe | 2023-08-14 21:01:40 +0000 | [diff] [blame] | 36 | #include "src/tint/lang/core/fluent_types.h" |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 37 | #include "src/tint/lang/core/ir/access.h" |
dan sinclair | 355ee9a | 2023-08-03 22:50:42 +0000 | [diff] [blame] | 38 | #include "src/tint/lang/core/ir/binary.h" |
dan sinclair | cea718c | 2023-11-22 16:08:34 +0000 | [diff] [blame] | 39 | #include "src/tint/lang/core/ir/bitcast.h" |
dan sinclair | 97c3727 | 2023-07-24 17:11:53 +0000 | [diff] [blame] | 40 | #include "src/tint/lang/core/ir/constant.h" |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 41 | #include "src/tint/lang/core/ir/construct.h" |
dan sinclair | eee3ad0 | 2023-11-20 13:22:41 +0000 | [diff] [blame] | 42 | #include "src/tint/lang/core/ir/discard.h" |
dan sinclair | 67d7e04 | 2023-08-02 13:45:25 +0000 | [diff] [blame] | 43 | #include "src/tint/lang/core/ir/exit_if.h" |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 44 | #include "src/tint/lang/core/ir/ice.h" |
dan sinclair | 67d7e04 | 2023-08-02 13:45:25 +0000 | [diff] [blame] | 45 | #include "src/tint/lang/core/ir/if.h" |
dan sinclair | 34e0342 | 2023-08-02 20:42:41 +0000 | [diff] [blame] | 46 | #include "src/tint/lang/core/ir/let.h" |
dan sinclair | be3b485 | 2023-08-03 19:58:33 +0000 | [diff] [blame] | 47 | #include "src/tint/lang/core/ir/load.h" |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 48 | #include "src/tint/lang/core/ir/module.h" |
dan sinclair | 3d0d6d5 | 2023-08-02 13:43:25 +0000 | [diff] [blame] | 49 | #include "src/tint/lang/core/ir/multi_in_block.h" |
| 50 | #include "src/tint/lang/core/ir/return.h" |
dan sinclair | 67d7e04 | 2023-08-02 13:45:25 +0000 | [diff] [blame] | 51 | #include "src/tint/lang/core/ir/unreachable.h" |
dan sinclair | 97c3727 | 2023-07-24 17:11:53 +0000 | [diff] [blame] | 52 | #include "src/tint/lang/core/ir/validator.h" |
dan sinclair | be3b485 | 2023-08-03 19:58:33 +0000 | [diff] [blame] | 53 | #include "src/tint/lang/core/ir/var.h" |
dan sinclair | 352f8c8 | 2023-07-21 00:40:07 +0000 | [diff] [blame] | 54 | #include "src/tint/lang/core/type/array.h" |
| 55 | #include "src/tint/lang/core/type/atomic.h" |
| 56 | #include "src/tint/lang/core/type/bool.h" |
| 57 | #include "src/tint/lang/core/type/depth_multisampled_texture.h" |
| 58 | #include "src/tint/lang/core/type/depth_texture.h" |
| 59 | #include "src/tint/lang/core/type/external_texture.h" |
| 60 | #include "src/tint/lang/core/type/f16.h" |
| 61 | #include "src/tint/lang/core/type/f32.h" |
| 62 | #include "src/tint/lang/core/type/i32.h" |
| 63 | #include "src/tint/lang/core/type/matrix.h" |
| 64 | #include "src/tint/lang/core/type/multisampled_texture.h" |
| 65 | #include "src/tint/lang/core/type/pointer.h" |
| 66 | #include "src/tint/lang/core/type/sampled_texture.h" |
| 67 | #include "src/tint/lang/core/type/storage_texture.h" |
| 68 | #include "src/tint/lang/core/type/texture.h" |
| 69 | #include "src/tint/lang/core/type/u32.h" |
| 70 | #include "src/tint/lang/core/type/vector.h" |
| 71 | #include "src/tint/lang/core/type/void.h" |
Ben Clayton | 4514677 | 2023-08-01 00:37:35 +0000 | [diff] [blame] | 72 | #include "src/tint/lang/msl/writer/common/printer_support.h" |
Ben Clayton | 34c8e57 | 2023-08-01 00:37:35 +0000 | [diff] [blame] | 73 | #include "src/tint/utils/containers/map.h" |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 74 | #include "src/tint/utils/generator/text_generator.h" |
dan sinclair | 22b4dd2 | 2023-07-21 00:40:07 +0000 | [diff] [blame] | 75 | #include "src/tint/utils/macros/scoped_assignment.h" |
| 76 | #include "src/tint/utils/rtti/switch.h" |
Ben Clayton | 34c8e57 | 2023-08-01 00:37:35 +0000 | [diff] [blame] | 77 | #include "src/tint/utils/text/string.h" |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 78 | |
dan sinclair | ce6dffe | 2023-08-14 21:01:40 +0000 | [diff] [blame] | 79 | using namespace tint::core::fluent_types; // NOLINT |
| 80 | |
dan sinclair | e7e4afc | 2023-07-26 02:28:30 +0000 | [diff] [blame] | 81 | namespace tint::msl::writer { |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 82 | namespace { |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 83 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 84 | /// PIMPL class for the MSL generator |
| 85 | class Printer : public tint::TextGenerator { |
| 86 | public: |
| 87 | /// Constructor |
| 88 | /// @param module the Tint IR module to generate |
| 89 | explicit Printer(core::ir::Module& module) : ir_(module) {} |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 90 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 91 | /// @returns the generated MSL shader |
| 92 | tint::Result<std::string> Generate() { |
| 93 | auto valid = core::ir::ValidateAndDumpIfNeeded(ir_, "MSL writer"); |
| 94 | if (!valid) { |
| 95 | return std::move(valid.Failure()); |
| 96 | } |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 97 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 98 | { |
| 99 | TINT_SCOPED_ASSIGNMENT(current_buffer_, &preamble_buffer_); |
| 100 | Line() << "#include <metal_stdlib>"; |
| 101 | Line() << "using namespace metal;"; |
| 102 | } |
| 103 | |
| 104 | // Emit module-scope declarations. |
| 105 | EmitBlockInstructions(ir_.root_block); |
| 106 | |
| 107 | // Emit functions. |
Ben Clayton | acef310 | 2023-11-20 17:09:35 +0000 | [diff] [blame] | 108 | for (auto& func : ir_.functions) { |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 109 | EmitFunction(func); |
| 110 | } |
| 111 | |
| 112 | StringStream ss; |
| 113 | ss << preamble_buffer_.String() << std::endl << main_buffer_.String(); |
| 114 | return ss.str(); |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 115 | } |
| 116 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 117 | private: |
| 118 | /// Map of builtin structure to unique generated name |
| 119 | std::unordered_map<const core::type::Struct*, std::string> builtin_struct_names_; |
| 120 | |
| 121 | core::ir::Module& ir_; |
| 122 | |
| 123 | /// The buffer holding preamble text |
| 124 | TextBuffer preamble_buffer_; |
| 125 | |
| 126 | /// Unique name of the 'TINT_INVARIANT' preprocessor define. |
| 127 | /// Non-empty only if an invariant attribute has been generated. |
| 128 | std::string invariant_define_name_; |
| 129 | |
| 130 | std::unordered_set<const core::type::Struct*> emitted_structs_; |
| 131 | |
| 132 | /// The current function being emitted |
| 133 | core::ir::Function* current_function_ = nullptr; |
| 134 | /// The current block being emitted |
| 135 | core::ir::Block* current_block_ = nullptr; |
| 136 | |
| 137 | /// Unique name of the tint_array<T, N> template. |
| 138 | /// Non-empty only if the template has been generated. |
| 139 | std::string array_template_name_; |
| 140 | |
| 141 | /// The representation for an IR pointer type |
| 142 | enum class PtrKind { |
| 143 | kPtr, // IR pointer is represented in a pointer |
| 144 | kRef, // IR pointer is represented in a reference |
| 145 | }; |
| 146 | |
| 147 | /// The structure for a value held by a 'let', 'var' or parameter. |
| 148 | struct VariableValue { |
| 149 | Symbol name; // Name of the variable |
| 150 | PtrKind ptr_kind = PtrKind::kRef; |
| 151 | }; |
| 152 | |
| 153 | /// The structure for an inlined value |
| 154 | struct InlinedValue { |
| 155 | std::string expr; |
| 156 | PtrKind ptr_kind = PtrKind::kRef; |
| 157 | }; |
| 158 | |
| 159 | /// Empty struct used as a sentinel value to indicate that an string expression has been |
| 160 | /// consumed by its single place of usage. Attempting to use this value a second time should |
| 161 | /// result in an ICE. |
| 162 | struct ConsumedValue {}; |
| 163 | |
| 164 | using ValueBinding = std::variant<VariableValue, InlinedValue, ConsumedValue>; |
| 165 | |
| 166 | /// IR values to their representation |
| 167 | Hashmap<core::ir::Value*, ValueBinding, 32> bindings_; |
| 168 | |
| 169 | /// Values that can be inlined. |
| 170 | Hashset<core::ir::Value*, 64> can_inline_; |
| 171 | |
| 172 | /// @returns the name of the templated `tint_array` helper type, generating it if needed |
| 173 | const std::string& ArrayTemplateName() { |
| 174 | if (!array_template_name_.empty()) { |
| 175 | return array_template_name_; |
| 176 | } |
| 177 | |
| 178 | array_template_name_ = UniqueIdentifier("tint_array"); |
| 179 | |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 180 | TINT_SCOPED_ASSIGNMENT(current_buffer_, &preamble_buffer_); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 181 | Line() << "template<typename T, size_t N>"; |
| 182 | Line() << "struct " << array_template_name_ << " {"; |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 183 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 184 | { |
| 185 | ScopedIndent si(current_buffer_); |
| 186 | Line() |
| 187 | << "const constant T& operator[](size_t i) const constant { return elements[i]; }"; |
| 188 | for (auto* space : {"device", "thread", "threadgroup"}) { |
| 189 | Line() << space << " T& operator[](size_t i) " << space |
| 190 | << " { return elements[i]; }"; |
| 191 | Line() << "const " << space << " T& operator[](size_t i) const " << space |
| 192 | << " { return elements[i]; }"; |
| 193 | } |
| 194 | Line() << "T elements[N];"; |
| 195 | } |
| 196 | Line() << "};"; |
| 197 | Line(); |
dan sinclair | cadd0c0 | 2023-06-26 13:50:42 +0000 | [diff] [blame] | 198 | |
dan sinclair | 83f2336 | 2023-06-26 21:02:35 +0000 | [diff] [blame] | 199 | return array_template_name_; |
| 200 | } |
| 201 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 202 | /// Emit the function |
| 203 | /// @param func the function to emit |
| 204 | void EmitFunction(core::ir::Function* func) { |
| 205 | TINT_SCOPED_ASSIGNMENT(current_function_, func); |
dan sinclair | 83f2336 | 2023-06-26 21:02:35 +0000 | [diff] [blame] | 206 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 207 | { |
| 208 | auto out = Line(); |
dan sinclair | 83f2336 | 2023-06-26 21:02:35 +0000 | [diff] [blame] | 209 | |
dan sinclair | 1fa6a4b | 2023-11-22 13:49:42 +0000 | [diff] [blame] | 210 | switch (func->Stage()) { |
| 211 | case core::ir::Function::PipelineStage::kCompute: |
| 212 | out << "kernel "; |
| 213 | break; |
| 214 | case core::ir::Function::PipelineStage::kFragment: |
| 215 | out << "fragment "; |
| 216 | break; |
| 217 | case core::ir::Function::PipelineStage::kVertex: |
| 218 | out << "vertex "; |
| 219 | break; |
| 220 | case core::ir::Function::PipelineStage::kUndefined: |
| 221 | break; |
| 222 | } |
| 223 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 224 | // TODO(dsinclair): Handle return type attributes |
| 225 | |
| 226 | EmitType(out, func->ReturnType()); |
| 227 | out << " " << ir_.NameOf(func).Name() << "() {"; |
| 228 | |
| 229 | // TODO(dsinclair): Emit Function parameters |
dan sinclair | 83f2336 | 2023-06-26 21:02:35 +0000 | [diff] [blame] | 230 | } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 231 | { |
| 232 | ScopedIndent si(current_buffer_); |
| 233 | EmitBlock(func->Block()); |
| 234 | } |
| 235 | |
| 236 | Line() << "}"; |
dan sinclair | 83f2336 | 2023-06-26 21:02:35 +0000 | [diff] [blame] | 237 | } |
dan sinclair | 83f2336 | 2023-06-26 21:02:35 +0000 | [diff] [blame] | 238 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 239 | /// Emit a block |
| 240 | /// @param block the block to emit |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 241 | void EmitBlock(core::ir::Block* block) { EmitBlockInstructions(block); } |
dan sinclair | 3d0d6d5 | 2023-08-02 13:43:25 +0000 | [diff] [blame] | 242 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 243 | /// Emit the instructions in a block |
| 244 | /// @param block the block with the instructions to emit |
| 245 | void EmitBlockInstructions(core::ir::Block* block) { |
| 246 | TINT_SCOPED_ASSIGNMENT(current_block_, block); |
| 247 | |
| 248 | for (auto* inst : *block) { |
| 249 | Switch( |
| 250 | inst, // |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 251 | [&](core::ir::ExitIf* e) { EmitExitIf(e); }, // |
| 252 | [&](core::ir::If* if_) { EmitIf(if_); }, // |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 253 | [&](core::ir::Return* r) { EmitReturn(r); }, // |
| 254 | [&](core::ir::Unreachable*) { EmitUnreachable(); }, // |
| 255 | [&](core::ir::Var* v) { EmitVar(v); }, // |
dan sinclair | eee3ad0 | 2023-11-20 13:22:41 +0000 | [diff] [blame] | 256 | [&](core::ir::Discard*) { EmitDiscard(); }, // |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 257 | |
dan sinclair | cea718c | 2023-11-22 16:08:34 +0000 | [diff] [blame] | 258 | [&](core::ir::Bitcast*) { MaybeEmitInstruction(inst); }, // |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 259 | [&](core::ir::Binary*) { MaybeEmitInstruction(inst); }, // |
| 260 | [&](core::ir::Let* l) { EmitLet(l); }, // |
| 261 | [&](core::ir::Load*) { MaybeEmitInstruction(inst); }, // |
| 262 | [&](core::ir::Construct*) { MaybeEmitInstruction(inst); }, // |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 263 | [&](core::ir::Access*) { MaybeEmitInstruction(inst); }, // |
Ben Clayton | d6082c5 | 2023-10-26 16:02:01 +0000 | [diff] [blame] | 264 | TINT_ICE_ON_NO_MATCH); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 265 | } |
| 266 | } |
| 267 | |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 268 | // If the instruction is named, we need to emit it. If it is un-named, then we'll use it |
| 269 | // and inline it later. |
| 270 | void MaybeEmitInstruction(const core::ir::Instruction* inst) { |
| 271 | auto name = ir_.NameOf(inst->Result()); |
| 272 | if (!name.IsValid()) { |
| 273 | return; |
| 274 | } |
| 275 | |
| 276 | auto out = Line(); |
| 277 | EmitType(out, inst->Result()->Type()); |
| 278 | out << " const " << name.Name() << " = "; |
| 279 | EmitValue(out, inst->Result()); |
| 280 | out << ";"; |
| 281 | } |
| 282 | |
| 283 | void EmitValue(StringStream& out, const core::ir::Value* v) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 284 | Switch( |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 285 | v, // |
| 286 | [&](const core::ir::Constant* c) { EmitConstant(out, c); }, // |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 287 | // [&](core::ir::FunctionParam* fp) {}, // |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 288 | [&](const core::ir::InstructionResult* r) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 289 | Switch( |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 290 | r->Source(), // |
| 291 | [&](const core::ir::Binary* b) { EmitBinary(out, b); }, // |
| 292 | [&](const core::ir::Let* l) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 293 | auto name = ir_.NameOf(l->Result()); |
| 294 | TINT_ASSERT(name.IsValid()); |
| 295 | out << name.Name(); |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 296 | }, // |
| 297 | [&](const core::ir::Load* l) { EmitValue(out, l->From()); }, // |
| 298 | [&](const core::ir::Construct* c) { EmitConstruct(out, c); }, // |
| 299 | [&](const core::ir::Var* var) { EmitVarName(out, var); }, // |
dan sinclair | cea718c | 2023-11-22 16:08:34 +0000 | [diff] [blame] | 300 | [&](const core::ir::Bitcast* b) { EmitBitcast(out, b); }, // |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 301 | [&](const core::ir::Access* a) { EmitAccess(out, a); }, // |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 302 | TINT_ICE_ON_NO_MATCH); |
| 303 | }, // |
| 304 | TINT_ICE_ON_NO_MATCH); |
| 305 | } |
| 306 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 307 | /// Emit a binary instruction |
| 308 | /// @param b the binary instruction |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 309 | void EmitBinary(StringStream& out, const core::ir::Binary* b) { |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 310 | if (b->Op() == core::ir::BinaryOp::kEqual) { |
| 311 | auto* rhs = b->RHS()->As<core::ir::Constant>(); |
| 312 | if (rhs && rhs->Type()->Is<core::type::Bool>() && |
| 313 | rhs->Value()->ValueAs<bool>() == false) { |
| 314 | // expr == false |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 315 | out << "!("; |
| 316 | EmitValue(out, b->LHS()); |
| 317 | out << ")"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 318 | return; |
| 319 | } |
| 320 | } |
| 321 | |
| 322 | auto kind = [&] { |
| 323 | switch (b->Op()) { |
| 324 | case core::ir::BinaryOp::kAdd: |
| 325 | return "+"; |
| 326 | case core::ir::BinaryOp::kSubtract: |
| 327 | return "-"; |
| 328 | case core::ir::BinaryOp::kMultiply: |
| 329 | return "*"; |
| 330 | case core::ir::BinaryOp::kDivide: |
| 331 | return "/"; |
| 332 | case core::ir::BinaryOp::kModulo: |
| 333 | return "%"; |
| 334 | case core::ir::BinaryOp::kAnd: |
| 335 | return "&"; |
| 336 | case core::ir::BinaryOp::kOr: |
| 337 | return "|"; |
| 338 | case core::ir::BinaryOp::kXor: |
| 339 | return "^"; |
| 340 | case core::ir::BinaryOp::kEqual: |
| 341 | return "=="; |
| 342 | case core::ir::BinaryOp::kNotEqual: |
| 343 | return "!="; |
| 344 | case core::ir::BinaryOp::kLessThan: |
| 345 | return "<"; |
| 346 | case core::ir::BinaryOp::kGreaterThan: |
| 347 | return ">"; |
| 348 | case core::ir::BinaryOp::kLessThanEqual: |
| 349 | return "<="; |
| 350 | case core::ir::BinaryOp::kGreaterThanEqual: |
| 351 | return ">="; |
| 352 | case core::ir::BinaryOp::kShiftLeft: |
| 353 | return "<<"; |
| 354 | case core::ir::BinaryOp::kShiftRight: |
| 355 | return ">>"; |
| 356 | } |
| 357 | return "<error>"; |
| 358 | }; |
| 359 | |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 360 | out << "("; |
| 361 | EmitValue(out, b->LHS()); |
| 362 | out << " " << kind() << " "; |
| 363 | EmitValue(out, b->RHS()); |
| 364 | out << ")"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 365 | } |
| 366 | |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 367 | void EmitVarName(StringStream& out, const core::ir::Var* v) { out << ir_.NameOf(v).Name(); } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 368 | |
| 369 | /// Emit a var instruction |
| 370 | /// @param v the var instruction |
| 371 | void EmitVar(core::ir::Var* v) { |
dan sinclair | 3d0d6d5 | 2023-08-02 13:43:25 +0000 | [diff] [blame] | 372 | auto out = Line(); |
| 373 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 374 | auto* ptr = v->Result()->Type()->As<core::type::Pointer>(); |
| 375 | TINT_ASSERT_OR_RETURN(ptr); |
dan sinclair | 3d0d6d5 | 2023-08-02 13:43:25 +0000 | [diff] [blame] | 376 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 377 | auto space = ptr->AddressSpace(); |
| 378 | switch (space) { |
| 379 | case core::AddressSpace::kFunction: |
| 380 | case core::AddressSpace::kHandle: |
| 381 | break; |
| 382 | case core::AddressSpace::kPrivate: |
| 383 | out << "thread "; |
| 384 | break; |
| 385 | case core::AddressSpace::kWorkgroup: |
| 386 | out << "threadgroup "; |
| 387 | break; |
| 388 | default: |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 389 | TINT_IR_ICE(ir_) << "unhandled variable address space"; |
dan sinclair | 7257c56 | 2023-06-28 01:03:24 +0000 | [diff] [blame] | 390 | return; |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 391 | } |
| 392 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 393 | auto name = ir_.NameOf(v); |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 394 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 395 | EmitType(out, ptr->UnwrapPtr()); |
| 396 | out << " " << name.Name(); |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 397 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 398 | if (v->Initializer()) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 399 | out << " = "; |
| 400 | EmitValue(out, v->Initializer()); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 401 | } else if (space == core::AddressSpace::kPrivate || |
| 402 | space == core::AddressSpace::kFunction || |
| 403 | space == core::AddressSpace::kUndefined) { |
| 404 | out << " = "; |
| 405 | EmitZeroValue(out, ptr->UnwrapPtr()); |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 406 | } |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 407 | out << ";"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 408 | } |
| 409 | |
| 410 | /// Emit a let instruction |
| 411 | /// @param l the let instruction |
| 412 | void EmitLet(core::ir::Let* l) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 413 | auto name = ir_.NameOf(l->Result()); |
| 414 | TINT_ASSERT(name.IsValid()); |
| 415 | |
| 416 | auto out = Line(); |
| 417 | EmitType(out, l->Result()->Type()); |
| 418 | out << " const " << name.Name() << " = "; |
| 419 | EmitValue(out, l->Value()); |
| 420 | out << ";"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 421 | } |
| 422 | |
| 423 | /// Emit an if instruction |
| 424 | /// @param if_ the if instruction |
| 425 | void EmitIf(core::ir::If* if_) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 426 | { |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 427 | auto out = Line(); |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 428 | out << "if ("; |
| 429 | EmitValue(out, if_->Condition()); |
| 430 | out << ") {"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 431 | } |
| 432 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 433 | { |
| 434 | ScopedIndent si(current_buffer_); |
| 435 | EmitBlockInstructions(if_->True()); |
| 436 | } |
| 437 | |
| 438 | if (if_->False() && !if_->False()->IsEmpty()) { |
| 439 | Line() << "} else {"; |
| 440 | |
| 441 | ScopedIndent si(current_buffer_); |
| 442 | EmitBlockInstructions(if_->False()); |
| 443 | } |
| 444 | |
| 445 | Line() << "}"; |
| 446 | } |
| 447 | |
| 448 | /// Emit an exit-if instruction |
| 449 | /// @param e the exit-if instruction |
| 450 | void EmitExitIf(core::ir::ExitIf* e) { |
| 451 | auto results = e->If()->Results(); |
| 452 | auto args = e->Args(); |
| 453 | for (size_t i = 0; i < e->Args().Length(); ++i) { |
| 454 | auto* phi = results[i]; |
| 455 | auto* val = args[i]; |
| 456 | |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 457 | auto out = Line(); |
| 458 | out << ir_.NameOf(phi).Name() << " = "; |
| 459 | EmitValue(out, val); |
| 460 | out << ";"; |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 461 | } |
| 462 | } |
| 463 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 464 | /// Emit a return instruction |
| 465 | /// @param r the return instruction |
| 466 | void EmitReturn(core::ir::Return* r) { |
| 467 | // If this return has no arguments and the current block is for the function which is |
| 468 | // being returned, skip the return. |
| 469 | if (current_block_ == current_function_->Block() && r->Args().IsEmpty()) { |
| 470 | return; |
| 471 | } |
| 472 | |
| 473 | auto out = Line(); |
| 474 | out << "return"; |
| 475 | if (!r->Args().IsEmpty()) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 476 | out << " "; |
| 477 | EmitValue(out, r->Args().Front()); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 478 | } |
| 479 | out << ";"; |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 480 | } |
| 481 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 482 | /// Emit an unreachable instruction |
| 483 | void EmitUnreachable() { Line() << "/* unreachable */"; } |
dan sinclair | 965321b | 2023-06-27 03:05:09 +0000 | [diff] [blame] | 484 | |
dan sinclair | eee3ad0 | 2023-11-20 13:22:41 +0000 | [diff] [blame] | 485 | /// Emit a discard instruction |
| 486 | void EmitDiscard() { Line() << "discard_fragment();"; } |
| 487 | |
dan sinclair | cea718c | 2023-11-22 16:08:34 +0000 | [diff] [blame] | 488 | /// Emit a bitcast instruction |
| 489 | void EmitBitcast(StringStream& out, const core::ir::Bitcast* b) { |
| 490 | out << "as_type<"; |
| 491 | EmitType(out, b->Result()->Type()); |
| 492 | out << ">("; |
| 493 | EmitValue(out, b->Val()); |
| 494 | out << ")"; |
| 495 | } |
| 496 | |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 497 | /// Emit an accessor |
| 498 | void EmitAccess(StringStream& out, const core::ir::Access* a) { |
| 499 | EmitValue(out, a->Object()); |
| 500 | |
dan sinclair | 3505ed4 | 2023-11-23 11:45:27 +0000 | [diff] [blame] | 501 | auto* current_type = a->Object()->Type(); |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 502 | for (auto* index : a->Indices()) { |
dan sinclair | 3505ed4 | 2023-11-23 11:45:27 +0000 | [diff] [blame] | 503 | TINT_ASSERT(current_type); |
| 504 | |
| 505 | current_type = current_type->UnwrapPtr(); |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 506 | Switch( |
| 507 | current_type, // |
| 508 | [&](const core::type::Struct* s) { |
| 509 | auto* c = index->As<core::ir::Constant>(); |
| 510 | auto* member = s->Members()[c->Value()->ValueAs<uint32_t>()]; |
| 511 | out << "." << member->Name().Name(); |
| 512 | current_type = member->Type(); |
| 513 | }, |
| 514 | [&](Default) { |
| 515 | out << "["; |
| 516 | EmitValue(out, index); |
| 517 | out << "]"; |
dan sinclair | 3505ed4 | 2023-11-23 11:45:27 +0000 | [diff] [blame] | 518 | current_type = current_type->Element(0); |
dan sinclair | 48318cd | 2023-11-23 11:27:32 +0000 | [diff] [blame] | 519 | }); |
| 520 | } |
| 521 | } |
| 522 | |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 523 | /// Emit a constructor |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 524 | void EmitConstruct(StringStream& out, const core::ir::Construct* c) { |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 525 | Switch( |
| 526 | c->Result()->Type(), |
| 527 | [&](const core::type::Array*) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 528 | EmitType(out, c->Result()->Type()); |
| 529 | out << "{"; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 530 | size_t i = 0; |
| 531 | for (auto* arg : c->Args()) { |
| 532 | if (i > 0) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 533 | out << ", "; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 534 | } |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 535 | EmitValue(out, arg); |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 536 | i++; |
| 537 | } |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 538 | out << "}"; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 539 | }, |
| 540 | [&](const core::type::Struct* struct_ty) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 541 | out << "{"; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 542 | size_t i = 0; |
| 543 | for (auto* arg : c->Args()) { |
| 544 | if (i > 0) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 545 | out << ", "; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 546 | } |
| 547 | // Emit field designators for structures to account for padding members. |
| 548 | auto name = struct_ty->Members()[i]->Name().Name(); |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 549 | out << "." << name << "="; |
| 550 | EmitValue(out, arg); |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 551 | i++; |
| 552 | } |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 553 | out << "}"; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 554 | }, |
| 555 | [&](Default) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 556 | EmitType(out, c->Result()->Type()); |
| 557 | out << "("; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 558 | size_t i = 0; |
| 559 | for (auto* arg : c->Args()) { |
| 560 | if (i > 0) { |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 561 | out << ", "; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 562 | } |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 563 | EmitValue(out, arg); |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 564 | i++; |
| 565 | } |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 566 | out << ")"; |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 567 | }); |
dan sinclair | 7bbe4c1 | 2023-11-21 12:11:32 +0000 | [diff] [blame] | 568 | } |
| 569 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 570 | /// Handles generating a address space |
| 571 | /// @param out the output of the type stream |
| 572 | /// @param sc the address space to generate |
| 573 | void EmitAddressSpace(StringStream& out, core::AddressSpace sc) { |
| 574 | switch (sc) { |
| 575 | case core::AddressSpace::kFunction: |
| 576 | case core::AddressSpace::kPrivate: |
| 577 | case core::AddressSpace::kHandle: |
| 578 | out << "thread"; |
| 579 | break; |
| 580 | case core::AddressSpace::kWorkgroup: |
| 581 | out << "threadgroup"; |
| 582 | break; |
| 583 | case core::AddressSpace::kStorage: |
| 584 | out << "device"; |
| 585 | break; |
| 586 | case core::AddressSpace::kUniform: |
| 587 | out << "constant"; |
| 588 | break; |
| 589 | default: |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 590 | TINT_IR_ICE(ir_) << "unhandled address space: " << sc; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 591 | break; |
dan sinclair | a356526 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 592 | } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 593 | } |
dan sinclair | a356526 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 594 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 595 | /// Emit a type |
| 596 | /// @param out the stream to emit too |
| 597 | /// @param ty the type to emit |
| 598 | void EmitType(StringStream& out, const core::type::Type* ty) { |
| 599 | tint::Switch( |
| 600 | ty, // |
| 601 | [&](const core::type::Bool*) { out << "bool"; }, // |
| 602 | [&](const core::type::Void*) { out << "void"; }, // |
| 603 | [&](const core::type::F32*) { out << "float"; }, // |
| 604 | [&](const core::type::F16*) { out << "half"; }, // |
| 605 | [&](const core::type::I32*) { out << "int"; }, // |
| 606 | [&](const core::type::U32*) { out << "uint"; }, // |
| 607 | [&](const core::type::Array* arr) { EmitArrayType(out, arr); }, |
| 608 | [&](const core::type::Vector* vec) { EmitVectorType(out, vec); }, |
| 609 | [&](const core::type::Matrix* mat) { EmitMatrixType(out, mat); }, |
| 610 | [&](const core::type::Atomic* atomic) { EmitAtomicType(out, atomic); }, |
| 611 | [&](const core::type::Pointer* ptr) { EmitPointerType(out, ptr); }, |
| 612 | [&](const core::type::Sampler*) { out << "sampler"; }, // |
| 613 | [&](const core::type::Texture* tex) { EmitTextureType(out, tex); }, |
| 614 | [&](const core::type::Struct* str) { |
| 615 | out << StructName(str); |
dan sinclair | ae33f97 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 616 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 617 | TINT_SCOPED_ASSIGNMENT(current_buffer_, &preamble_buffer_); |
| 618 | EmitStructType(str); |
Ben Clayton | d6082c5 | 2023-10-26 16:02:01 +0000 | [diff] [blame] | 619 | }, // |
| 620 | TINT_ICE_ON_NO_MATCH); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 621 | } |
dan sinclair | 6767459 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 622 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 623 | /// Handles generating a pointer declaration |
| 624 | /// @param out the output stream |
| 625 | /// @param ptr the pointer to emit |
| 626 | void EmitPointerType(StringStream& out, const core::type::Pointer* ptr) { |
| 627 | if (ptr->Access() == core::Access::kRead) { |
| 628 | out << "const "; |
| 629 | } |
| 630 | EmitAddressSpace(out, ptr->AddressSpace()); |
| 631 | out << " "; |
| 632 | EmitType(out, ptr->StoreType()); |
| 633 | out << "*"; |
| 634 | } |
dan sinclair | 6767459 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 635 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 636 | /// Handles generating an atomic declaration |
| 637 | /// @param out the output stream |
| 638 | /// @param atomic the atomic to emit |
| 639 | void EmitAtomicType(StringStream& out, const core::type::Atomic* atomic) { |
| 640 | if (atomic->Type()->Is<core::type::I32>()) { |
| 641 | out << "atomic_int"; |
| 642 | return; |
| 643 | } |
| 644 | if (TINT_LIKELY(atomic->Type()->Is<core::type::U32>())) { |
| 645 | out << "atomic_uint"; |
| 646 | return; |
| 647 | } |
| 648 | TINT_ICE() << "unhandled atomic type " << atomic->Type()->FriendlyName(); |
| 649 | } |
| 650 | |
| 651 | /// Handles generating an array declaration |
| 652 | /// @param out the output stream |
| 653 | /// @param arr the array to emit |
| 654 | void EmitArrayType(StringStream& out, const core::type::Array* arr) { |
| 655 | out << ArrayTemplateName() << "<"; |
| 656 | EmitType(out, arr->ElemType()); |
| 657 | out << ", "; |
| 658 | if (arr->Count()->Is<core::type::RuntimeArrayCount>()) { |
| 659 | out << "1"; |
| 660 | } else { |
| 661 | auto count = arr->ConstantCount(); |
dan sinclair | 6767459 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 662 | if (!count) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 663 | TINT_IR_ICE(ir_) << core::type::Array::kErrExpectedConstantCount; |
dan sinclair | 6767459 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 664 | return; |
| 665 | } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 666 | out << count.value(); |
| 667 | } |
| 668 | out << ">"; |
| 669 | } |
dan sinclair | 45b59a8 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 670 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 671 | /// Handles generating a vector declaration |
| 672 | /// @param out the output stream |
| 673 | /// @param vec the vector to emit |
| 674 | void EmitVectorType(StringStream& out, const core::type::Vector* vec) { |
| 675 | if (vec->Packed()) { |
| 676 | out << "packed_"; |
| 677 | } |
| 678 | EmitType(out, vec->type()); |
| 679 | out << vec->Width(); |
| 680 | } |
| 681 | |
| 682 | /// Handles generating a matrix declaration |
| 683 | /// @param out the output stream |
| 684 | /// @param mat the matrix to emit |
| 685 | void EmitMatrixType(StringStream& out, const core::type::Matrix* mat) { |
| 686 | EmitType(out, mat->type()); |
| 687 | out << mat->columns() << "x" << mat->rows(); |
| 688 | } |
| 689 | |
| 690 | /// Handles generating a texture declaration |
| 691 | /// @param out the output stream |
| 692 | /// @param tex the texture to emit |
| 693 | void EmitTextureType(StringStream& out, const core::type::Texture* tex) { |
| 694 | if (TINT_UNLIKELY(tex->Is<core::type::ExternalTexture>())) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 695 | TINT_IR_ICE(ir_) << "Multiplanar external texture transform was not run."; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 696 | return; |
| 697 | } |
| 698 | |
| 699 | if (tex->IsAnyOf<core::type::DepthTexture, core::type::DepthMultisampledTexture>()) { |
| 700 | out << "depth"; |
| 701 | } else { |
| 702 | out << "texture"; |
| 703 | } |
| 704 | |
| 705 | switch (tex->dim()) { |
| 706 | case core::type::TextureDimension::k1d: |
| 707 | out << "1d"; |
| 708 | break; |
| 709 | case core::type::TextureDimension::k2d: |
| 710 | out << "2d"; |
| 711 | break; |
| 712 | case core::type::TextureDimension::k2dArray: |
| 713 | out << "2d_array"; |
| 714 | break; |
| 715 | case core::type::TextureDimension::k3d: |
| 716 | out << "3d"; |
| 717 | break; |
| 718 | case core::type::TextureDimension::kCube: |
| 719 | out << "cube"; |
| 720 | break; |
| 721 | case core::type::TextureDimension::kCubeArray: |
| 722 | out << "cube_array"; |
| 723 | break; |
| 724 | default: |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 725 | TINT_IR_ICE(ir_) << "invalid texture dimensions"; |
dan sinclair | 45b59a8 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 726 | return; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 727 | } |
| 728 | if (tex->IsAnyOf<core::type::MultisampledTexture, core::type::DepthMultisampledTexture>()) { |
| 729 | out << "_ms"; |
| 730 | } |
| 731 | out << "<"; |
| 732 | TINT_DEFER(out << ">"); |
| 733 | |
| 734 | tint::Switch( |
| 735 | tex, // |
| 736 | [&](const core::type::DepthTexture*) { out << "float, access::sample"; }, |
| 737 | [&](const core::type::DepthMultisampledTexture*) { out << "float, access::read"; }, |
| 738 | [&](const core::type::StorageTexture* storage) { |
| 739 | EmitType(out, storage->type()); |
| 740 | out << ", "; |
| 741 | |
| 742 | std::string access_str; |
| 743 | if (storage->access() == core::Access::kRead) { |
| 744 | out << "access::read"; |
| 745 | } else if (storage->access() == core::Access::kWrite) { |
| 746 | out << "access::write"; |
| 747 | } else { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 748 | TINT_IR_ICE(ir_) << "invalid access control for storage texture"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 749 | return; |
| 750 | } |
| 751 | }, |
| 752 | [&](const core::type::MultisampledTexture* ms) { |
| 753 | EmitType(out, ms->type()); |
| 754 | out << ", access::read"; |
| 755 | }, |
| 756 | [&](const core::type::SampledTexture* sampled) { |
| 757 | EmitType(out, sampled->type()); |
| 758 | out << ", access::sample"; |
Ben Clayton | d6082c5 | 2023-10-26 16:02:01 +0000 | [diff] [blame] | 759 | }, // |
| 760 | TINT_ICE_ON_NO_MATCH); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 761 | } |
| 762 | |
| 763 | /// Handles generating a struct declaration. If the structure has already been emitted, then |
| 764 | /// this function will simply return without emitting anything. |
| 765 | /// @param str the struct to generate |
| 766 | void EmitStructType(const core::type::Struct* str) { |
| 767 | auto it = emitted_structs_.emplace(str); |
| 768 | if (!it.second) { |
| 769 | return; |
| 770 | } |
| 771 | |
| 772 | // This does not append directly to the preamble because a struct may require other |
| 773 | // structs, or the array template, to get emitted before it. So, the struct emits into a |
| 774 | // temporary text buffer, then anything it depends on will emit to the preamble first, |
| 775 | // and then it copies the text buffer into the preamble. |
| 776 | TextBuffer str_buf; |
| 777 | Line(&str_buf) << "struct " << StructName(str) << " {"; |
| 778 | |
| 779 | bool is_host_shareable = str->IsHostShareable(); |
| 780 | |
| 781 | // Emits a `/* 0xnnnn */` byte offset comment for a struct member. |
| 782 | auto add_byte_offset_comment = [&](StringStream& out, uint32_t offset) { |
| 783 | std::ios_base::fmtflags saved_flag_state(out.flags()); |
| 784 | out << "/* 0x" << std::hex << std::setfill('0') << std::setw(4) << offset << " */ "; |
| 785 | out.flags(saved_flag_state); |
| 786 | }; |
| 787 | |
| 788 | auto add_padding = [&](uint32_t size, uint32_t msl_offset) { |
| 789 | std::string name; |
| 790 | do { |
| 791 | name = UniqueIdentifier("tint_pad"); |
| 792 | } while (str->FindMember(ir_.symbols.Get(name))); |
| 793 | |
| 794 | auto out = Line(&str_buf); |
| 795 | add_byte_offset_comment(out, msl_offset); |
| 796 | out << ArrayTemplateName() << "<int8_t, " << size << "> " << name << ";"; |
| 797 | }; |
| 798 | |
| 799 | str_buf.IncrementIndent(); |
| 800 | |
| 801 | uint32_t msl_offset = 0; |
| 802 | for (auto* mem : str->Members()) { |
| 803 | auto out = Line(&str_buf); |
| 804 | auto mem_name = mem->Name().Name(); |
| 805 | auto ir_offset = mem->Offset(); |
| 806 | |
| 807 | if (is_host_shareable) { |
| 808 | if (TINT_UNLIKELY(ir_offset < msl_offset)) { |
| 809 | // Unimplementable layout |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 810 | TINT_IR_ICE(ir_) << "Structure member offset (" << ir_offset |
| 811 | << ") is behind MSL offset (" << msl_offset << ")"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 812 | return; |
| 813 | } |
| 814 | |
| 815 | // Generate padding if required |
| 816 | if (auto padding = ir_offset - msl_offset) { |
| 817 | add_padding(padding, msl_offset); |
| 818 | msl_offset += padding; |
| 819 | } |
| 820 | |
| 821 | add_byte_offset_comment(out, msl_offset); |
dan sinclair | 45b59a8 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 822 | } |
| 823 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 824 | auto* ty = mem->Type(); |
| 825 | |
| 826 | EmitType(out, ty); |
| 827 | out << " " << mem_name; |
| 828 | |
| 829 | // Emit attributes |
| 830 | auto& attributes = mem->Attributes(); |
| 831 | |
| 832 | if (auto builtin = attributes.builtin) { |
| 833 | auto name = BuiltinToAttribute(builtin.value()); |
| 834 | if (name.empty()) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 835 | TINT_IR_ICE(ir_) << "unknown builtin"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 836 | return; |
| 837 | } |
| 838 | out << " [[" << name << "]]"; |
| 839 | } |
| 840 | |
| 841 | if (auto location = attributes.location) { |
| 842 | auto& pipeline_stage_uses = str->PipelineStageUses(); |
| 843 | if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 844 | TINT_IR_ICE(ir_) << "invalid entry point IO struct uses"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 845 | return; |
| 846 | } |
| 847 | |
| 848 | if (pipeline_stage_uses.count(core::type::PipelineStageUsage::kVertexInput)) { |
| 849 | out << " [[attribute(" + std::to_string(location.value()) + ")]]"; |
| 850 | } else if (pipeline_stage_uses.count( |
| 851 | core::type::PipelineStageUsage::kVertexOutput)) { |
| 852 | out << " [[user(locn" + std::to_string(location.value()) + ")]]"; |
| 853 | } else if (pipeline_stage_uses.count( |
| 854 | core::type::PipelineStageUsage::kFragmentInput)) { |
| 855 | out << " [[user(locn" + std::to_string(location.value()) + ")]]"; |
| 856 | } else if (TINT_LIKELY(pipeline_stage_uses.count( |
| 857 | core::type::PipelineStageUsage::kFragmentOutput))) { |
| 858 | out << " [[color(" + std::to_string(location.value()) + ")]]"; |
| 859 | } else { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 860 | TINT_IR_ICE(ir_) << "invalid use of location decoration"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 861 | return; |
| 862 | } |
| 863 | } |
| 864 | |
| 865 | if (auto interpolation = attributes.interpolation) { |
| 866 | auto name = InterpolationToAttribute(interpolation->type, interpolation->sampling); |
| 867 | if (name.empty()) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 868 | TINT_IR_ICE(ir_) << "unknown interpolation attribute"; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 869 | return; |
| 870 | } |
| 871 | out << " [[" << name << "]]"; |
| 872 | } |
| 873 | |
| 874 | if (attributes.invariant) { |
| 875 | invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT"); |
| 876 | out << " " << invariant_define_name_; |
| 877 | } |
| 878 | |
| 879 | out << ";"; |
| 880 | |
| 881 | if (is_host_shareable) { |
| 882 | // Calculate new MSL offset |
| 883 | auto size_align = MslPackedTypeSizeAndAlign(ty); |
| 884 | if (TINT_UNLIKELY(msl_offset % size_align.align)) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 885 | TINT_IR_ICE(ir_) << "Misaligned MSL structure member " << mem_name << " : " |
| 886 | << ty->FriendlyName() << " offset: " << msl_offset |
| 887 | << " align: " << size_align.align; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 888 | return; |
| 889 | } |
| 890 | msl_offset += size_align.size; |
| 891 | } |
| 892 | } |
| 893 | |
| 894 | if (is_host_shareable && str->Size() != msl_offset) { |
| 895 | add_padding(str->Size() - msl_offset, msl_offset); |
| 896 | } |
| 897 | |
| 898 | str_buf.DecrementIndent(); |
| 899 | Line(&str_buf) << "};"; |
| 900 | |
| 901 | preamble_buffer_.Append(str_buf); |
| 902 | } |
| 903 | |
| 904 | /// Handles core::ir::Constant values |
| 905 | /// @param out the stream to write the constant too |
| 906 | /// @param c the constant to emit |
dan sinclair | 5555694 | 2023-11-22 13:13:42 +0000 | [diff] [blame] | 907 | void EmitConstant(StringStream& out, const core::ir::Constant* c) { |
| 908 | EmitConstant(out, c->Value()); |
| 909 | } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 910 | |
| 911 | /// Handles core::constant::Value values |
| 912 | /// @param out the stream to write the constant too |
| 913 | /// @param c the constant to emit |
| 914 | void EmitConstant(StringStream& out, const core::constant::Value* c) { |
| 915 | auto emit_values = [&](uint32_t count) { |
| 916 | for (size_t i = 0; i < count; i++) { |
dan sinclair | 45b59a8 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 917 | if (i > 0) { |
| 918 | out << ", "; |
| 919 | } |
dan sinclair | 45b59a8 | 2023-06-27 13:18:13 +0000 | [diff] [blame] | 920 | EmitConstant(out, c->Index(i)); |
| 921 | } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 922 | }; |
dan sinclair | ed70ac0 | 2023-06-27 03:48:03 +0000 | [diff] [blame] | 923 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 924 | tint::Switch( |
| 925 | c->Type(), // |
| 926 | [&](const core::type::Bool*) { out << (c->ValueAs<bool>() ? "true" : "false"); }, |
| 927 | [&](const core::type::I32*) { PrintI32(out, c->ValueAs<i32>()); }, |
| 928 | [&](const core::type::U32*) { out << c->ValueAs<u32>() << "u"; }, |
| 929 | [&](const core::type::F32*) { PrintF32(out, c->ValueAs<f32>()); }, |
| 930 | [&](const core::type::F16*) { PrintF16(out, c->ValueAs<f16>()); }, |
| 931 | [&](const core::type::Vector* v) { |
| 932 | EmitType(out, v); |
dan sinclair | be3b485 | 2023-08-03 19:58:33 +0000 | [diff] [blame] | 933 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 934 | ScopedParen sp(out); |
| 935 | if (auto* splat = c->As<core::constant::Splat>()) { |
| 936 | EmitConstant(out, splat->el); |
| 937 | return; |
| 938 | } |
| 939 | emit_values(v->Width()); |
| 940 | }, |
| 941 | [&](const core::type::Matrix* m) { |
| 942 | EmitType(out, m); |
| 943 | ScopedParen sp(out); |
| 944 | emit_values(m->columns()); |
| 945 | }, |
| 946 | [&](const core::type::Array* a) { |
| 947 | EmitType(out, a); |
| 948 | out << "{"; |
| 949 | TINT_DEFER(out << "}"); |
dan sinclair | be3b485 | 2023-08-03 19:58:33 +0000 | [diff] [blame] | 950 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 951 | if (c->AllZero()) { |
| 952 | return; |
| 953 | } |
| 954 | |
| 955 | auto count = a->ConstantCount(); |
| 956 | if (!count) { |
dan sinclair | 019146f | 2023-11-20 23:47:42 +0000 | [diff] [blame] | 957 | TINT_IR_ICE(ir_) << core::type::Array::kErrExpectedConstantCount; |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 958 | return; |
| 959 | } |
| 960 | emit_values(*count); |
| 961 | }, |
| 962 | [&](const core::type::Struct* s) { |
| 963 | EmitStructType(s); |
| 964 | out << StructName(s) << "{"; |
| 965 | TINT_DEFER(out << "}"); |
| 966 | |
| 967 | if (c->AllZero()) { |
| 968 | return; |
| 969 | } |
| 970 | |
| 971 | auto members = s->Members(); |
| 972 | for (size_t i = 0; i < members.Length(); i++) { |
| 973 | if (i > 0) { |
| 974 | out << ", "; |
| 975 | } |
| 976 | out << "." << members[i]->Name().Name() << "="; |
| 977 | EmitConstant(out, c->Index(i)); |
| 978 | } |
Ben Clayton | d6082c5 | 2023-10-26 16:02:01 +0000 | [diff] [blame] | 979 | }, // |
| 980 | TINT_ICE_ON_NO_MATCH); |
Ben Clayton | 34c8e57 | 2023-08-01 00:37:35 +0000 | [diff] [blame] | 981 | } |
Ben Clayton | 34c8e57 | 2023-08-01 00:37:35 +0000 | [diff] [blame] | 982 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 983 | /// Emits the zero value for the given type |
| 984 | /// @param out the stream to emit too |
| 985 | /// @param ty the type |
| 986 | void EmitZeroValue(StringStream& out, const core::type::Type* ty) { |
| 987 | Switch( |
| 988 | ty, [&](const core::type::Bool*) { out << "false"; }, // |
| 989 | [&](const core::type::F16*) { out << "0.0h"; }, // |
| 990 | [&](const core::type::F32*) { out << "0.0f"; }, // |
| 991 | [&](const core::type::I32*) { out << "0"; }, // |
| 992 | [&](const core::type::U32*) { out << "0u"; }, // |
| 993 | [&](const core::type::Vector* vec) { EmitZeroValue(out, vec->type()); }, // |
| 994 | [&](const core::type::Matrix* mat) { |
| 995 | EmitType(out, mat); |
Ben Clayton | 6755230 | 2023-07-25 20:48:37 +0000 | [diff] [blame] | 996 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 997 | ScopedParen sp(out); |
| 998 | EmitZeroValue(out, mat->type()); |
| 999 | }, |
| 1000 | [&](const core::type::Array*) { out << "{}"; }, // |
| 1001 | [&](const core::type::Struct*) { out << "{}"; }, // |
Ben Clayton | d6082c5 | 2023-10-26 16:02:01 +0000 | [diff] [blame] | 1002 | TINT_ICE_ON_NO_MATCH); |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 1003 | } |
dan sinclair | 155a42a | 2023-08-02 20:23:58 +0000 | [diff] [blame] | 1004 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 1005 | /// @param s the structure |
| 1006 | /// @returns the name of the structure, taking special care of builtin structures that start |
| 1007 | /// with double underscores. If the structure is a builtin, then the returned name will be a |
| 1008 | /// unique name without the leading underscores. |
| 1009 | std::string StructName(const core::type::Struct* s) { |
| 1010 | auto name = s->Name().Name(); |
| 1011 | if (HasPrefix(name, "__")) { |
| 1012 | name = tint::GetOrCreate(builtin_struct_names_, s, |
| 1013 | [&] { return UniqueIdentifier(name.substr(2)); }); |
| 1014 | } |
| 1015 | return name; |
| 1016 | } |
dan sinclair | 155a42a | 2023-08-02 20:23:58 +0000 | [diff] [blame] | 1017 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 1018 | /// @return a new, unique identifier with the given prefix. |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 1019 | /// @param prefix optional prefix to apply to the generated identifier. If empty |
| 1020 | /// "tint_symbol" will be used. |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 1021 | std::string UniqueIdentifier(const std::string& prefix /* = "" */) { |
| 1022 | return ir_.symbols.New(prefix).Name(); |
| 1023 | } |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 1024 | }; |
dan sinclair | 58794b5 | 2023-11-21 14:35:44 +0000 | [diff] [blame] | 1025 | |
Ben Clayton | d8054e2 | 2023-10-26 14:24:30 +0000 | [diff] [blame] | 1026 | } // namespace |
| 1027 | |
| 1028 | Result<std::string> Print(core::ir::Module& module) { |
| 1029 | return Printer{module}.Generate(); |
dan sinclair | 355ee9a | 2023-08-03 22:50:42 +0000 | [diff] [blame] | 1030 | } |
| 1031 | |
dan sinclair | e7e4afc | 2023-07-26 02:28:30 +0000 | [diff] [blame] | 1032 | } // namespace tint::msl::writer |