1 | /* |
2 | * Copyright (C) 2019 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "WHLSLIntrinsics.h" |
28 | |
29 | #if ENABLE(WEBGPU) |
30 | |
31 | #include "WHLSLConstantExpression.h" |
32 | #include "WHLSLTypeArgument.h" |
33 | #include "WHLSLTypeReference.h" |
34 | #include <algorithm> |
35 | #include <cstring> |
36 | |
37 | namespace WebCore { |
38 | |
39 | namespace WHLSL { |
40 | |
41 | constexpr const char* Intrinsics::m_textureTypeNames[]; |
42 | constexpr const char* Intrinsics::m_textureInnerTypeNames[]; |
43 | constexpr const char* Intrinsics::m_depthTextureInnerTypes[]; |
44 | |
45 | Intrinsics::Intrinsics() |
46 | { |
47 | } |
48 | |
49 | void Intrinsics::add(AST::NativeFunctionDeclaration& nativeFunctionDeclaration) |
50 | { |
51 | if (nativeFunctionDeclaration.name() == "ddx" ) |
52 | m_ddx = &nativeFunctionDeclaration; |
53 | else if (nativeFunctionDeclaration.name() == "ddy" ) |
54 | m_ddy = &nativeFunctionDeclaration; |
55 | else if (nativeFunctionDeclaration.name() == "AllMemoryBarrierWithGroupSync" ) |
56 | m_allMemoryBarrier = &nativeFunctionDeclaration; |
57 | else if (nativeFunctionDeclaration.name() == "DeviceMemoryBarrierWithGroupSync" ) |
58 | m_deviceMemoryBarrier = &nativeFunctionDeclaration; |
59 | else if (nativeFunctionDeclaration.name() == "GroupMemoryBarrierWithGroupSync" ) |
60 | m_groupMemoryBarrier = &nativeFunctionDeclaration; |
61 | } |
62 | |
63 | bool Intrinsics::addPrimitive(AST::NativeTypeDeclaration& nativeTypeDeclaration) |
64 | { |
65 | if (nativeTypeDeclaration.typeArguments().size()) |
66 | return false; |
67 | |
68 | if (nativeTypeDeclaration.name() == "void" ) |
69 | m_voidType = &nativeTypeDeclaration; |
70 | else if (nativeTypeDeclaration.name() == "bool" ) |
71 | m_boolType = &nativeTypeDeclaration; |
72 | else if (nativeTypeDeclaration.name() == "uchar" ) { |
73 | nativeTypeDeclaration.setIsInt(); |
74 | nativeTypeDeclaration.setIsNumber(); |
75 | nativeTypeDeclaration.setCanRepresentInteger([](int x) { |
76 | return x >= 0 && x <= 0xFF; |
77 | }); |
78 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned x) { |
79 | return x <= 0xFF; |
80 | }); |
81 | nativeTypeDeclaration.setCanRepresentFloat([](float x) { |
82 | return static_cast<float>(static_cast<uint8_t>(x)) == x; |
83 | }); |
84 | nativeTypeDeclaration.setSuccessor([](int64_t x) -> int64_t { |
85 | return static_cast<uint8_t>(x + 1); |
86 | }); |
87 | nativeTypeDeclaration.setFormatValueFromInteger([](int x) -> int64_t { |
88 | return static_cast<uint8_t>(x); |
89 | }); |
90 | nativeTypeDeclaration.setFormatValueFromUnsignedInteger([](unsigned x) -> int64_t { |
91 | return static_cast<uint8_t>(x); |
92 | }); |
93 | nativeTypeDeclaration.setIterateAllValues([](const std::function<bool(int64_t)>& callback) { |
94 | for (int64_t i = 0; i < 0x100; ++i) { |
95 | if (callback(i)) |
96 | break; |
97 | } |
98 | }); |
99 | m_ucharType = &nativeTypeDeclaration; |
100 | } else if (nativeTypeDeclaration.name() == "ushort" ) { |
101 | nativeTypeDeclaration.setIsInt(); |
102 | nativeTypeDeclaration.setIsNumber(); |
103 | nativeTypeDeclaration.setCanRepresentInteger([](int x) { |
104 | return x >= 0 && x <= 0xFFFF; |
105 | }); |
106 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned x) { |
107 | return x <= 0xFFFF; |
108 | }); |
109 | nativeTypeDeclaration.setCanRepresentFloat([](float x) { |
110 | return static_cast<float>(static_cast<uint16_t>(x)) == x; |
111 | }); |
112 | nativeTypeDeclaration.setSuccessor([](int64_t x) -> int64_t { |
113 | return static_cast<uint16_t>(x + 1); |
114 | }); |
115 | nativeTypeDeclaration.setFormatValueFromInteger([](int x) -> int64_t { |
116 | return static_cast<uint16_t>(x); |
117 | }); |
118 | nativeTypeDeclaration.setFormatValueFromUnsignedInteger([](unsigned x) -> int64_t { |
119 | return static_cast<uint16_t>(x); |
120 | }); |
121 | nativeTypeDeclaration.setIterateAllValues([](const std::function<bool(int64_t)>& callback) { |
122 | for (int64_t i = 0; i < 0x10000; ++i) { |
123 | if (callback(i)) |
124 | break; |
125 | } |
126 | }); |
127 | m_ushortType = &nativeTypeDeclaration; |
128 | } else if (nativeTypeDeclaration.name() == "uint" ) { |
129 | nativeTypeDeclaration.setIsInt(); |
130 | nativeTypeDeclaration.setIsNumber(); |
131 | nativeTypeDeclaration.setCanRepresentInteger([](int x) { |
132 | return x >= 0; |
133 | }); |
134 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned) { |
135 | return true; |
136 | }); |
137 | nativeTypeDeclaration.setCanRepresentFloat([](float x) { |
138 | return static_cast<float>(static_cast<uint32_t>(x)) == x; |
139 | }); |
140 | nativeTypeDeclaration.setSuccessor([](int64_t x) -> int64_t { |
141 | return static_cast<uint32_t>(x + 1); |
142 | }); |
143 | nativeTypeDeclaration.setFormatValueFromInteger([](int x) -> int64_t { |
144 | return static_cast<uint32_t>(x); |
145 | }); |
146 | nativeTypeDeclaration.setFormatValueFromUnsignedInteger([](unsigned x) -> int64_t { |
147 | return static_cast<uint32_t>(x); |
148 | }); |
149 | nativeTypeDeclaration.setIterateAllValues([](const std::function<bool(int64_t)>& callback) { |
150 | for (int64_t i = 0; i < 0x100000000; ++i) { |
151 | if (callback(i)) |
152 | break; |
153 | } |
154 | }); |
155 | m_uintType = &nativeTypeDeclaration; |
156 | } else if (nativeTypeDeclaration.name() == "char" ) { |
157 | nativeTypeDeclaration.setIsInt(); |
158 | nativeTypeDeclaration.setIsNumber(); |
159 | nativeTypeDeclaration.setIsSigned(); |
160 | nativeTypeDeclaration.setCanRepresentInteger([](int x) { |
161 | return x >= -128 && x <= 127; |
162 | }); |
163 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned x) { |
164 | return x <= 127; |
165 | }); |
166 | nativeTypeDeclaration.setCanRepresentFloat([](float x) { |
167 | return static_cast<float>(static_cast<int8_t>(x)) == x; |
168 | }); |
169 | nativeTypeDeclaration.setSuccessor([](int64_t x) -> int64_t { |
170 | return static_cast<int8_t>(x + 1); |
171 | }); |
172 | nativeTypeDeclaration.setFormatValueFromInteger([](int x) -> int64_t { |
173 | return static_cast<int8_t>(x); |
174 | }); |
175 | nativeTypeDeclaration.setFormatValueFromUnsignedInteger([](unsigned x) -> int64_t { |
176 | return static_cast<int8_t>(x); |
177 | }); |
178 | nativeTypeDeclaration.setIterateAllValues([](const std::function<bool(int64_t)>& callback) { |
179 | for (int64_t i = -128; i < 128; ++i) { |
180 | if (callback(i)) |
181 | break; |
182 | } |
183 | }); |
184 | m_charType = &nativeTypeDeclaration; |
185 | } else if (nativeTypeDeclaration.name() == "short" ) { |
186 | nativeTypeDeclaration.setIsInt(); |
187 | nativeTypeDeclaration.setIsNumber(); |
188 | nativeTypeDeclaration.setIsSigned(); |
189 | nativeTypeDeclaration.setCanRepresentInteger([](int x) { |
190 | return x >= -32768 && x <= 32767; |
191 | }); |
192 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned x) { |
193 | return x <= 32767; |
194 | }); |
195 | nativeTypeDeclaration.setCanRepresentFloat([](float x) { |
196 | return static_cast<float>(static_cast<int16_t>(x)) == x; |
197 | }); |
198 | nativeTypeDeclaration.setSuccessor([](int64_t x) -> int64_t { |
199 | return static_cast<int16_t>(x + 1); |
200 | }); |
201 | nativeTypeDeclaration.setFormatValueFromInteger([](int x) -> int64_t { |
202 | return static_cast<int16_t>(x); |
203 | }); |
204 | nativeTypeDeclaration.setFormatValueFromUnsignedInteger([](unsigned x) -> int64_t { |
205 | return static_cast<int16_t>(x); |
206 | }); |
207 | nativeTypeDeclaration.setIterateAllValues([](const std::function<bool(int64_t)>& callback) { |
208 | for (int64_t i = -32768; i < 32768; ++i) { |
209 | if (callback(i)) |
210 | break; |
211 | } |
212 | }); |
213 | m_shortType = &nativeTypeDeclaration; |
214 | } else if (nativeTypeDeclaration.name() == "int" ) { |
215 | nativeTypeDeclaration.setIsInt(); |
216 | nativeTypeDeclaration.setIsNumber(); |
217 | nativeTypeDeclaration.setIsSigned(); |
218 | nativeTypeDeclaration.setCanRepresentInteger([](int) { |
219 | return true; |
220 | }); |
221 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned x) { |
222 | return x <= 2147483647; |
223 | }); |
224 | nativeTypeDeclaration.setCanRepresentFloat([](float x) { |
225 | return static_cast<float>(static_cast<int32_t>(x)) == x; |
226 | }); |
227 | nativeTypeDeclaration.setSuccessor([](int64_t x) -> int64_t { |
228 | return static_cast<int32_t>(x + 1); |
229 | }); |
230 | nativeTypeDeclaration.setFormatValueFromInteger([](int x) -> int64_t { |
231 | return static_cast<int32_t>(x); |
232 | }); |
233 | nativeTypeDeclaration.setFormatValueFromUnsignedInteger([](unsigned x) -> int64_t { |
234 | return static_cast<int32_t>(x); |
235 | }); |
236 | nativeTypeDeclaration.setIterateAllValues([](const std::function<bool(int64_t)>& callback) { |
237 | for (int64_t i = -2147483647; i < 2147483648; ++i) { |
238 | if (callback(i)) |
239 | break; |
240 | } |
241 | }); |
242 | m_intType = &nativeTypeDeclaration; |
243 | } else if (nativeTypeDeclaration.name() == "half" ) { |
244 | nativeTypeDeclaration.setIsNumber(); |
245 | nativeTypeDeclaration.setIsFloating(); |
246 | nativeTypeDeclaration.setCanRepresentInteger([](int) { |
247 | return true; |
248 | }); |
249 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned) { |
250 | return true; |
251 | }); |
252 | nativeTypeDeclaration.setCanRepresentFloat([](float) { |
253 | return true; |
254 | }); |
255 | m_halfType = &nativeTypeDeclaration; |
256 | } else if (nativeTypeDeclaration.name() == "float" ) { |
257 | nativeTypeDeclaration.setIsNumber(); |
258 | nativeTypeDeclaration.setIsFloating(); |
259 | nativeTypeDeclaration.setCanRepresentInteger([](int) { |
260 | return true; |
261 | }); |
262 | nativeTypeDeclaration.setCanRepresentUnsignedInteger([](unsigned) { |
263 | return true; |
264 | }); |
265 | nativeTypeDeclaration.setCanRepresentFloat([](float) { |
266 | return true; |
267 | }); |
268 | m_floatType = &nativeTypeDeclaration; |
269 | } else if (nativeTypeDeclaration.name() == "atomic_int" ) { |
270 | nativeTypeDeclaration.setIsAtomic(); |
271 | m_atomicIntType = &nativeTypeDeclaration; |
272 | } else if (nativeTypeDeclaration.name() == "atomic_uint" ) { |
273 | nativeTypeDeclaration.setIsAtomic(); |
274 | m_atomicUintType = &nativeTypeDeclaration; |
275 | } else if (nativeTypeDeclaration.name() == "sampler" ) |
276 | m_samplerType = &nativeTypeDeclaration; |
277 | else |
278 | ASSERT_NOT_REACHED(); |
279 | return true; |
280 | } |
281 | |
282 | bool Intrinsics::addVector(AST::NativeTypeDeclaration& nativeTypeDeclaration) |
283 | { |
284 | if (nativeTypeDeclaration.name() != "vector" ) |
285 | return false; |
286 | |
287 | ASSERT(nativeTypeDeclaration.typeArguments().size() == 2); |
288 | ASSERT(WTF::holds_alternative<UniqueRef<AST::TypeReference>>(nativeTypeDeclaration.typeArguments()[0])); |
289 | ASSERT(WTF::holds_alternative<AST::ConstantExpression>(nativeTypeDeclaration.typeArguments()[1])); |
290 | auto& innerType = static_cast<AST::TypeReference&>(WTF::get<UniqueRef<AST::TypeReference>>(nativeTypeDeclaration.typeArguments()[0])); |
291 | auto& lengthExpression = WTF::get<AST::ConstantExpression>(nativeTypeDeclaration.typeArguments()[1]); |
292 | ASSERT(!innerType.typeArguments().size()); |
293 | AST::NativeTypeDeclaration** array; |
294 | if (innerType.name() == "bool" ) |
295 | array = m_vectorBool; |
296 | else if (innerType.name() == "uchar" ) |
297 | array = m_vectorUchar; |
298 | else if (innerType.name() == "ushort" ) |
299 | array = m_vectorUshort; |
300 | else if (innerType.name() == "uint" ) |
301 | array = m_vectorUint; |
302 | else if (innerType.name() == "char" ) |
303 | array = m_vectorChar; |
304 | else if (innerType.name() == "short" ) |
305 | array = m_vectorShort; |
306 | else if (innerType.name() == "int" ) |
307 | array = m_vectorInt; |
308 | else if (innerType.name() == "half" ) |
309 | array = m_vectorHalf; |
310 | else { |
311 | ASSERT(innerType.name() == "float" ); |
312 | array = m_vectorFloat; |
313 | } |
314 | int length = lengthExpression.integerLiteral().value(); |
315 | ASSERT(length >= 2 && length <= 4); |
316 | nativeTypeDeclaration.setIsVector(); |
317 | array[length - 2] = &nativeTypeDeclaration; |
318 | return true; |
319 | } |
320 | |
321 | bool Intrinsics::addMatrix(AST::NativeTypeDeclaration& nativeTypeDeclaration) |
322 | { |
323 | if (nativeTypeDeclaration.name() != "matrix" ) |
324 | return false; |
325 | |
326 | ASSERT(nativeTypeDeclaration.typeArguments().size() == 3); |
327 | ASSERT(WTF::holds_alternative<UniqueRef<AST::TypeReference>>(nativeTypeDeclaration.typeArguments()[0])); |
328 | ASSERT(WTF::holds_alternative<AST::ConstantExpression>(nativeTypeDeclaration.typeArguments()[1])); |
329 | ASSERT(WTF::holds_alternative<AST::ConstantExpression>(nativeTypeDeclaration.typeArguments()[2])); |
330 | auto& innerType = static_cast<AST::TypeReference&>(WTF::get<UniqueRef<AST::TypeReference>>(nativeTypeDeclaration.typeArguments()[0])); |
331 | auto& rowExpression = WTF::get<AST::ConstantExpression>(nativeTypeDeclaration.typeArguments()[1]); |
332 | auto& columnExpression = WTF::get<AST::ConstantExpression>(nativeTypeDeclaration.typeArguments()[2]); |
333 | ASSERT(!innerType.typeArguments().size()); |
334 | AST::NativeTypeDeclaration* (*array)[3]; |
335 | if (innerType.name() == "half" ) |
336 | array = m_matrixHalf; |
337 | if (innerType.name() == "float" ) |
338 | array = m_matrixFloat; |
339 | int row = rowExpression.integerLiteral().value(); |
340 | ASSERT(row >= 2 && row <= 4); |
341 | int column = columnExpression.integerLiteral().value(); |
342 | ASSERT(column >= 2 && column <= 4); |
343 | nativeTypeDeclaration.setIsMatrix(); |
344 | array[row - 2][column - 2] = &nativeTypeDeclaration; |
345 | return true; |
346 | } |
347 | |
348 | bool Intrinsics::addFullTexture(AST::NativeTypeDeclaration& nativeTypeDeclaration, AST::TypeReference& innerType) |
349 | { |
350 | unsigned textureTypeIndex = WTF_ARRAY_LENGTH(m_textureTypeNames); |
351 | for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_textureTypeNames); ++i) { |
352 | if (nativeTypeDeclaration.name() == m_textureTypeNames[i]) |
353 | textureTypeIndex = i; |
354 | } |
355 | if (textureTypeIndex == WTF_ARRAY_LENGTH(m_textureTypeNames)) |
356 | return false; |
357 | |
358 | unsigned innerTypeIndex = WTF_ARRAY_LENGTH(m_textureInnerTypeNames); |
359 | unsigned vectorLength; |
360 | for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_textureInnerTypeNames); ++i) { |
361 | if (innerType.name().startsWith(m_textureInnerTypeNames[i])) { |
362 | innerTypeIndex = i; |
363 | if (innerType.name() == m_textureInnerTypeNames[i]) |
364 | vectorLength = 1; |
365 | else { |
366 | ASSERT(innerType.name().length() == strlen(m_textureInnerTypeNames[i]) + 1); |
367 | ASSERT(innerType.name()[innerType.name().length() - 1] == '2' |
368 | || innerType.name()[innerType.name().length() - 1] == '3' |
369 | || innerType.name()[innerType.name().length() - 1] == '4'); |
370 | vectorLength = innerType.name()[innerType.name().length() - 1] - '0'; |
371 | } |
372 | } |
373 | } |
374 | ASSERT(innerTypeIndex != WTF_ARRAY_LENGTH(m_textureInnerTypeNames)); |
375 | nativeTypeDeclaration.setIsTexture(); |
376 | m_fullTextures[textureTypeIndex][innerTypeIndex][vectorLength - 1] = &nativeTypeDeclaration; |
377 | return true; |
378 | } |
379 | |
380 | bool Intrinsics::addDepthTexture(AST::NativeTypeDeclaration& nativeTypeDeclaration, AST::TypeReference& innerType) |
381 | { |
382 | AST::NativeTypeDeclaration** texture; |
383 | if (nativeTypeDeclaration.name() == "TextureDepth2D" ) |
384 | texture = m_textureDepth2D; |
385 | else if (nativeTypeDeclaration.name() == "RWTextureDepth2D" ) |
386 | texture = m_rwTextureDepth2D; |
387 | else if (nativeTypeDeclaration.name() == "TextureDepth2DArray" ) |
388 | texture = m_textureDepth2DArray; |
389 | else if (nativeTypeDeclaration.name() == "RWTextureDepth2DArray" ) |
390 | texture = m_rwTextureDepth2DArray; |
391 | else if (nativeTypeDeclaration.name() == "TextureDepthCube" ) |
392 | texture = m_textureDepthCube; |
393 | else |
394 | ASSERT_NOT_REACHED(); |
395 | unsigned innerTypeIndex = WTF_ARRAY_LENGTH(m_depthTextureInnerTypes); |
396 | for (unsigned i = 0; i < WTF_ARRAY_LENGTH(m_depthTextureInnerTypes); ++i) { |
397 | if (innerType.name() == m_depthTextureInnerTypes[i]) |
398 | innerTypeIndex = i; |
399 | } |
400 | ASSERT(innerTypeIndex != WTF_ARRAY_LENGTH(m_depthTextureInnerTypes)); |
401 | nativeTypeDeclaration.setIsTexture(); |
402 | texture[innerTypeIndex] = &nativeTypeDeclaration; |
403 | return true; |
404 | } |
405 | |
406 | void Intrinsics::addTexture(AST::NativeTypeDeclaration& nativeTypeDeclaration) |
407 | { |
408 | ASSERT(nativeTypeDeclaration.typeArguments().size() == 1); |
409 | ASSERT(WTF::holds_alternative<UniqueRef<AST::TypeReference>>(nativeTypeDeclaration.typeArguments()[0])); |
410 | auto& innerType = static_cast<AST::TypeReference&>(WTF::get<UniqueRef<AST::TypeReference>>(nativeTypeDeclaration.typeArguments()[0])); |
411 | ASSERT(!innerType.typeArguments().size()); |
412 | if (addFullTexture(nativeTypeDeclaration, innerType)) { |
413 | m_textureSet.add(&nativeTypeDeclaration); |
414 | return; |
415 | } |
416 | if (addDepthTexture(nativeTypeDeclaration, innerType)) |
417 | m_textureSet.add(&nativeTypeDeclaration); |
418 | } |
419 | |
420 | void Intrinsics::add(AST::NativeTypeDeclaration& nativeTypeDeclaration) |
421 | { |
422 | if (addPrimitive(nativeTypeDeclaration)) |
423 | return; |
424 | if (addVector(nativeTypeDeclaration)) |
425 | return; |
426 | if (addMatrix(nativeTypeDeclaration)) |
427 | return; |
428 | addTexture(nativeTypeDeclaration); |
429 | } |
430 | |
431 | } // namespace WHLSL |
432 | |
433 | } // namespace WebCore |
434 | |
435 | #endif // ENABLE(WEBGPU) |
436 | |