WebKit Bugzilla
Attachment 350174 Details for
Bug 187735
: [WHLSL] Metal code generation
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-187735-20180919232734.patch (text/plain), 98.77 KB, created by
Thomas Denney
on 2018-09-19 23:27:39 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Thomas Denney
Created:
2018-09-19 23:27:39 PDT
Size:
98.77 KB
patch
obsolete
>Subversion Revision: 236237 >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 3daefaa5511ca5f7c24f72eb339580bc970a80b0..7d5c867f805c8cc6b7101b9c7e9f4123c61fb503 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,50 @@ >+2018-09-19 Thomas Denney <tdenney@apple.com> >+ >+ [WHLSL] Metal code generation >+ https://bugs.webkit.org/show_bug.cgi?id=187735 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Adds support for generating Metal Shading Language from WHLSL. Clients >+ should include the file MetalCodegenAll.js and then call whlslToMsl >+ with their program source code to compile to Metal. >+ >+ * WebGPUShadingLanguageRI/ArrayType.js: >+ (ArrayType.prototype.get arrayRefType): Adds the arrayRefType method to >+ all types to find the type of that expression when it is used in a >+ MakeArrayRefExpression. >+ * WebGPUShadingLanguageRI/MakeArrayRefExpression.js: >+ (MakeArrayRefExpression): >+ (MakeArrayRefExpression.prototype.get type): Uses the new arrayRefType >+ getter on all types to find the type of the expression. >+ * WebGPUShadingLanguageRI/Metal/MSLBackend.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLCodegenAll.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLCompileResult.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLConstexprEmitter.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLNameMangler.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLNativeFunctionCall.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLTypeAttributes.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLTypeAttributesMap.js: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLTypeUnifier.js: Added. >+ * WebGPUShadingLanguageRI/Metal/TypeOf.js: Added. >+ * WebGPUShadingLanguageRI/Metal/WhlslToMsl.js: Added. >+ * WebGPUShadingLanguageRI/PropertyResolver.js: >+ * WebGPUShadingLanguageRI/SynthesizeStructAccessors.js: >+ * WebGPUShadingLanguageRI/Test.js: Added awkward tests for the compiler >+ to generate code for. >+ (tests.incrementAndDecrement): >+ (tests.returnIntLiteralUint): >+ (tests.returnIntLiteralFloat): >+ (tests.nestedSubscriptWithArraysInStructs): >+ (tests.nestedSubscript): >+ (tests.lotsOfLocalVariables): >+ * WebGPUShadingLanguageRI/Type.js: >+ (Type.prototype.get arrayRefType): See above. >+ > 2018-09-19 Chris Dumez <cdumez@apple.com> > > Crash under WebProcessProxy::suspendedPageWasDestroyed(WebKit::SuspendedPageProxy&) >diff --git a/Tools/WebGPUShadingLanguageRI/ArrayType.js b/Tools/WebGPUShadingLanguageRI/ArrayType.js >index 0d56a0b375712c4c14bf731196109dff586bab94..b9d918e80cd210a937f238bde4f4668f10689360 100644 >--- a/Tools/WebGPUShadingLanguageRI/ArrayType.js >+++ b/Tools/WebGPUShadingLanguageRI/ArrayType.js >@@ -48,6 +48,11 @@ class ArrayType extends Type { > return this.numElements.value; > } > >+ get arrayRefType() >+ { >+ return new ArrayRefType(this.origin, "thread", this.elementType); >+ } >+ > toString() > { > return this.elementType + "[" + this.numElements + "]"; >diff --git a/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js b/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js >index 0cc6f6ecd23bf03f0ae223f192b1a07ad443709e..6eb6f27d26ccd27c86f362f3808c5ec5f1ef8a56 100644 >--- a/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js >+++ b/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js >@@ -29,12 +29,13 @@ class MakeArrayRefExpression extends Expression { > { > super(origin); > this._lValue = lValue; >- if (this.lValue.variable && this.lValue.variable.type && this.lValue.variable.type.isArray && this.lValue.variable.type.elementType) { >- this._type = new ArrayRefType(origin, "thread", this.lValue.variable.type.elementType); >- } > } > >- get type() { return this._type; } >+ get type() >+ { >+ return typeOf(this.lValue).arrayRefType; >+ } >+ > get lValue() { return this._lValue; } > > toString() >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLBackend.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLBackend.js >new file mode 100644 >index 0000000000000000000000000000000000000000..5030a9d33f93204e20e1d256cdd6c8f0c35f3802 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLBackend.js >@@ -0,0 +1,420 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+const DefaultMetalSource = `#include <metal_stdlib> >+using namespace metal; >+ >+`; >+ >+// Handles the compilation of Program AST instances to MSLCompileResult instances, which >+// include the raw MSL. In general clients should call |whlslToMsl|, which parses, >+// typechecks, and inlines the WHLSL before passing it to this compiler. >+class MSLBackend { >+ >+ constructor(program) >+ { >+ this._program = program; >+ this._declarations = []; >+ this._funcNameMangler = new MSLNameMangler("F"); >+ this._typeUnifier = new MSLTypeUnifier(); >+ this._forwardTypeDecls = []; >+ this._typeDefinitions = []; >+ this._forwardFunctionDecls = []; >+ this._functionDefintions = []; >+ } >+ >+ get program() >+ { >+ return this._program; >+ } >+ >+ get declarations() >+ { >+ return this._declarations; >+ } >+ >+ get functionSources() >+ { >+ return this._functionSources; >+ } >+ >+ compile() >+ { >+ try { >+ const src = this._msl(); >+ const mangledMap = {}; >+ for (let func of this._functionDefintions) { >+ const key = func.func.name; >+ const value = this._funcNameMangler.mangle(func.func); >+ mangledMap[key] = value; >+ } >+ >+ return new MSLCompileResult(src, null, mangledMap, this._functionSources); >+ } catch (e) { >+ return new MSLCompileResult(null, e, null, null); >+ } >+ } >+ >+ _msl() >+ { >+ const functionsToCompile = this._findFunctionsToCompile(); >+ for (let func of functionsToCompile) >+ func.visit(this._typeUnifier); >+ >+ this._allTypeAttributes = new MSLTypeAttributesMap(functionsToCompile, this._typeUnifier); >+ this._createTypeDecls(); >+ this._createFunctionDecls(functionsToCompile); >+ >+ let outputStr = DefaultMetalSource; >+ >+ const addSection = (title, decls) => { >+ outputStr += `#pragma mark - ${title}\n\n${decls.join("\n\n")}\n\n`; >+ }; >+ >+ addSection("Forward type declarations", this._forwardTypeDecls); >+ addSection("Type definitions", this._typeDefinitions); >+ addSection("Forward function declarations", this._forwardFunctionDecls); >+ addSection("Function definitions", this._functionDefintions); >+ >+ if (!this._allowComments) >+ outputStr = this._removeCommentsAndEmptyLines(outputStr); >+ >+ return outputStr; >+ } >+ >+ _findFunctionsToCompile() >+ { >+ const entryPointFunctions = []; >+ for (let [name, instances] of this._program.functions) { >+ for (let instance of instances) { >+ if (instance.isEntryPoint) >+ entryPointFunctions.push(instance); >+ } >+ } >+ const functions = new Set(entryPointFunctions); >+ >+ class FindFunctionsThatGetCalled extends Visitor { >+ visitCallExpression(node) >+ { >+ super.visitCallExpression(node); >+ if (node.func instanceof FuncDef) { >+ functions.add(node.func); >+ node.func.visit(this); >+ } >+ } >+ } >+ const findFunctionsThatGetCalledVisitor = new FindFunctionsThatGetCalled(); >+ for (let entryPoint of entryPointFunctions) >+ entryPoint.visit(findFunctionsThatGetCalledVisitor); >+ return Array.from(functions); >+ } >+ >+ _createTypeDecls() >+ { >+ const typesThatNeedDeclaration = this._typeUnifier.typesThatNeedDeclaration(); >+ const typeDeclsInOrderInTypeDefOrder = this._sortTypeDefsAndForwardDeclarationsInTopologicalOrder(typesThatNeedDeclaration); >+ for (let type of typeDeclsInOrderInTypeDefOrder) { >+ if (type instanceof StructType) >+ this._forwardTypeDecls.push(this._metalSourceForStructForwardDeclaration(type)); >+ else if (type instanceof ArrayRefType) >+ this._forwardTypeDecls.push(this._metalSourceForArrayRefForwardDeclaration(type)); >+ else >+ this._forwardTypeDecls.push(this._metalSourceForTypeDef(type)); >+ } >+ >+ const typeDeclsThatNeedDeclarationInOrder = this._sortTypeDeclarationsInTopologicalOrder(typesThatNeedDeclaration); >+ for (let type of typeDeclsThatNeedDeclarationInOrder) { >+ if (type instanceof StructType) >+ this._typeDefinitions.push(this._metalSourceForStructDefinition(type)); >+ else if (type instanceof ArrayRefType) >+ this._typeDefinitions.push(this._metalSourceForArrayRefDefinition(type)); >+ } >+ } >+ >+ _createFunctionDecls(unifiedFunctionDefs) >+ { >+ for (let func of unifiedFunctionDefs) { >+ this._forwardFunctionDecls.push(new MSLFunctionForwardDeclaration(this._funcNameMangler, func, this._typeUnifier, this._allTypeAttributes)); >+ this._functionDefintions.push(new MSLFunctionDefinition(this._funcNameMangler, func, this._typeUnifier, this._allTypeAttributes)); >+ } >+ } >+ >+ _sortTypeDefsAndForwardDeclarationsInTopologicalOrder(typesToDeclare) >+ { >+ // All types are sorted in topological order in this method; every type needs a typedef. >+ // We do not recurse into structs becasue this is *just* for forward declarations. >+ const declarations = new Array(); >+ for (let type of typesToDeclare) >+ declarations.push(type); >+ >+ let typeOrder = []; >+ let visitedSet = new Set(); >+ const typeUnifier = this._typeUnifier; >+ class TypeOrderer extends Visitor { >+ _visitType(node, visitCallback) >+ { >+ const id = typeUnifier.uniqueTypeId(node); >+ if (!visitedSet.has(id)) { >+ visitedSet.add(id); >+ visitCallback(); >+ typeOrder.push(node); >+ } >+ } >+ >+ visitNativeType(node) >+ { >+ this._visitType(node, () => {}); >+ } >+ >+ visitEnumType(node) >+ { >+ node.baseType.visit(this); >+ } >+ >+ visitArrayType(node) >+ { >+ this._visitType(node, () => super.visitArrayType(node)); >+ } >+ >+ visitVectorType(node) >+ { >+ this._visitType(node, () => {}); >+ } >+ >+ visitMatrixType(node) >+ { >+ this._visitType(node, () => {}); >+ } >+ >+ visitStructType(node) >+ { >+ // Don't need to recurse into elements for the forward declarations. >+ this._visitType(node, () => {}); >+ } >+ >+ visitArrayRefType(node) >+ { >+ this._visitType(node, () => super.visitArrayRefType(node)); >+ } >+ >+ visitReferenceType(node) >+ { >+ this._visitType(node, () => super.visitReferenceType(node)); >+ } >+ >+ visitTypeRef(node) >+ { >+ super.visitTypeRef(node); >+ node.type.visit(this); >+ } >+ } >+ const typeOrderer = new TypeOrderer(); >+ for (let type of typesToDeclare) >+ type.visit(typeOrderer); >+ return typeOrder; >+ } >+ >+ _sortTypeDeclarationsInTopologicalOrder(typesToDeclare) >+ { >+ const declarations = new Array(); >+ for (let type of typesToDeclare) >+ declarations.push(type); >+ >+ let typeOrder = []; >+ let visitedSet = new Set(); >+ const typeUnifier = this._typeUnifier; >+ class TypeOrderer extends Visitor { >+ _visitType(node, visitCallback) >+ { >+ const id = typeUnifier.uniqueTypeId(node); >+ if (!visitedSet.has(id)) { >+ visitedSet.add(id); >+ visitCallback(); >+ typeOrder.push(node); >+ } >+ } >+ >+ visitStructType(node) >+ { >+ this._visitType(node, () => super.visitStructType(node)); >+ } >+ >+ visitTypeRef(node) >+ { >+ super.visitTypeRef(node); >+ node.type.visit(this); >+ } >+ >+ visitArrayRefType(node) >+ { >+ this._visitType(node, () => super.visitArrayRefType(node)); >+ } >+ >+ visitReferenceType(node) >+ { >+ // We need an empty implementation to avoid recursion. >+ } >+ } >+ const typeOrderer = new TypeOrderer(); >+ for (let type of typesToDeclare) >+ type.visit(typeOrderer); >+ >+ const typeOrderMap = new Map(); >+ for (let i = 0; i < typeOrder.length; i++) >+ typeOrderMap.set(typeOrder[i], i); >+ >+ return typeOrder; >+ } >+ >+ // Also removes #pragma marks. >+ _removeCommentsAndEmptyLines(src) >+ { >+ const singleLineCommentRegex = /(\/\/|\#pragma)(.*?)($|\n)/; >+ const lines = src.split('\n') >+ .map(line => line.replace(singleLineCommentRegex, '').trimEnd()) >+ .filter(line => line.length > 0); >+ return lines.join('\n'); >+ } >+ >+ _metalSourceForArrayRefDefinition(arrayRefType) >+ { >+ let src = `struct ${this._typeUnifier.uniqueTypeId(arrayRefType)} {\n`; >+ const fakePtrType = new PtrType(arrayRefType.origin, arrayRefType.addressSpace, arrayRefType.elementType); >+ src += ` ${this._typeUnifier.uniqueTypeId(fakePtrType)} ptr;\n` >+ src += " uint32_t length;\n"; >+ src += "};"; >+ return src; >+ } >+ >+ _metalSourceForArrayRefForwardDeclaration(arrayRefType) >+ { >+ return `struct ${this._typeUnifier.uniqueTypeId(arrayRefType)};`; >+ } >+ >+ _metalSourceForStructForwardDeclaration(structType) >+ { >+ return `struct ${this._typeUnifier.uniqueTypeId(structType)};`; >+ } >+ >+ _metalSourceForStructDefinition(structType) >+ { >+ const structTypeAttributes = this._allTypeAttributes.attributesForType(structType); >+ let src = `struct ${this._typeUnifier.uniqueTypeId(structType)} {\n`; >+ >+ let index = 0; >+ for (let [fieldName, field] of structType.fieldMap) { >+ const mangledFieldName = structTypeAttributes.mangledFieldName(fieldName); >+ src += ` ${this._typeUnifier.uniqueTypeId(field.type)} ${mangledFieldName}`; >+ >+ const annotations = []; >+ if (structTypeAttributes.isVertexAttribute) >+ annotations.push(`attribute(${index++})`); >+ if (structTypeAttributes.isVertexOutputOrFragmentInput && fieldName === "wsl_Position") >+ annotations.push("position"); >+ if (structTypeAttributes.isFragmentOutput && fieldName === "wsl_Color") >+ annotations.push("color(0)"); >+ if (annotations.length) >+ src += ` [[${annotations.join(", ")}]]`; >+ src += `; // ${fieldName} (${field.type}) \n`; >+ } >+ >+ src += "};"; >+ >+ return src; >+ } >+ >+ _metalSourceForTypeDef(node) >+ { >+ const name = this._typeUnifier.uniqueTypeId(node); >+ if (node.isArray) >+ return `typedef ${this._typeUnifier.uniqueTypeId(node.elementType)} ${name}[${node.numElementsValue}];`; >+ else if (node.isPtr) >+ return `typedef ${node.addressSpace} ${this._typeUnifier.uniqueTypeId(node.elementType)} (*${name});`; >+ else { >+ class NativeTypeNameVisitor extends Visitor { >+ visitNativeType(node) >+ { >+ // FIXME: Also add samplers and textures here. >+ const nativeTypeNameMap = { >+ "void": "void", >+ "bool": "bool", >+ "uchar": "uint8_t", >+ "ushort": "uint16_t", >+ "uint": "uint32_t", >+ "char": "int8_t", >+ "short": "int16_t", >+ "int": "int32_t", >+ "half": "half", >+ "float": "float", >+ "atomic_int": "atomic_int", >+ "atomic_uint": "atomic_uint" >+ }; >+ >+ return nativeTypeNameMap[node.name]; >+ } >+ >+ visitVectorType(node) >+ { >+ // Vector type names work slightly differently to native type names. >+ const elementTypeNameMap = { >+ "bool": "bool", >+ "char": "char", >+ "uchar": "uchar", >+ "short": "short", >+ "ushort": "ushort", >+ "int": "int", >+ "uint": "uint", >+ "half": "half", >+ "float": "float" >+ }; >+ >+ const elementTypeName = elementTypeNameMap[node.elementType.name]; >+ if (!elementTypeName) >+ throw new Error(`${node.elementType.name} is not a supported vector element type`); >+ >+ return `${elementTypeName}${node.numElementsValue}`; >+ } >+ >+ visitMatrixType(node) >+ { >+ const elementTypeNameMap = { >+ "half": "half", >+ "float": "float" >+ }; >+ >+ const elementTypeName = elementTypeNameMap[node.elementType.name]; >+ if (!elementTypeName) >+ throw new Error(`${node.elementType.name} is not a supported matrix element type`); >+ >+ return `${elementTypeName}${node.numRowsValue}x${node.numColumnsValue}`; >+ } >+ } >+ const nativeName = node.visit(new NativeTypeNameVisitor()); >+ if (!nativeName) >+ throw new Error(`Native type name not found for ${node}`); >+ return `typedef ${nativeName} ${name};`; >+ } >+ } >+} >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLCodegenAll.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLCodegenAll.js >new file mode 100644 >index 0000000000000000000000000000000000000000..e59d3dea39c91fdd4b736275a8d6f887b1473c10 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLCodegenAll.js >@@ -0,0 +1,39 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+load("MSLBackend.js"); >+load("MSLCompileResult.js"); >+load("MSLConstexprEmitter.js"); >+load("MSLFunctionDeclaration.js"); >+load("MSLFunctionDefinition.js"); >+load("MSLFunctionForwardDeclaration.js"); >+load("MSLNameMangler.js"); >+load("MSLNativeFunctionCall.js"); >+load("MSLStatementEmitter.js"); >+load("MSLTypeAttributesMap.js"); >+load("MSLTypeAttributes.js"); >+load("MSLTypeUnifier.js"); >+load("TypeOf.js"); >+load("WhlslToMsl.js"); >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLCompileResult.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLCompileResult.js >new file mode 100644 >index 0000000000000000000000000000000000000000..2fd0bb892b669e79b730c790f453ae5a29346d0c >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLCompileResult.js >@@ -0,0 +1,60 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+class MSLCompileResult { >+ >+ constructor(src, err, mangledNameMap, functionSources) >+ { >+ this._metalShaderLanguageSource = src; >+ this._error = err; >+ this._originalFunctionNameToMangledNames = mangledNameMap; >+ this._functionSources = functionSources; >+ } >+ >+ get metalShaderLanguageSource() >+ { >+ return this._metalShaderLanguageSource; >+ } >+ >+ get error() >+ { >+ return this._error; >+ } >+ >+ get originalFunctionNameToMangledNames() >+ { >+ return this._originalFunctionNameToMangledNames; >+ } >+ >+ get functionSources() >+ { >+ return this._functionSources; >+ } >+ >+ get didSucceed() >+ { >+ return !this.error; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLConstexprEmitter.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLConstexprEmitter.js >new file mode 100644 >index 0000000000000000000000000000000000000000..5d343286cfd2f38a43e83c49059804478bbfe9d1 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLConstexprEmitter.js >@@ -0,0 +1,54 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+// Used in scenarios where having an auxiliary variable is not possible (e.g. switch cases). >+class MSLConstexprEmitter extends Visitor >+{ >+ visitIdentityExpression(node) >+ { >+ return node.target.visit(this); >+ } >+ >+ visitBoolLiteral(node) >+ { >+ return node.value.toString(); >+ } >+ >+ visitEnumLiteral(node) >+ { >+ return node.member.value.visit(this); >+ } >+ >+ visitGenericLiteral(node) >+ { >+ // FIXME: What happens in the case of halfs/floats/etc >+ return node.value.toString(); >+ } >+ >+ visitNullLiteral(node) >+ { >+ return "nullptr"; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.js >new file mode 100644 >index 0000000000000000000000000000000000000000..7c07f44838c73e8e2cd41d90fac76a505512087e >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.js >@@ -0,0 +1,124 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+// Emits code for the first line of a function declaration or definition. >+class MSLFunctionDeclaration { >+ >+ constructor(funcMangler, funcDef, typeUnifier, typeAttributes) >+ { >+ this._funcMangler = funcMangler; >+ this._func = funcDef; >+ this._typeUnifier = typeUnifier; >+ this._typeAttributes = typeAttributes; >+ } >+ >+ get funcMangler() >+ { >+ return this._funcMangler; >+ } >+ >+ get func() >+ { >+ return this._func; >+ } >+ >+ get typeUnifier() >+ { >+ return this._typeUnifier; >+ } >+ >+ get typeAttributes() >+ { >+ return this._typeAttributes; >+ } >+ >+ get isVertexShader() >+ { >+ return this._func.shaderType == "vertex"; >+ } >+ >+ get paramMap() >+ { >+ const map = new Map(); >+ let counter = 0; >+ for (let param of this._func.parameters) >+ map.set(param, `P${counter++}`); >+ return map; >+ } >+ >+ commentLine() >+ { >+ const params = []; >+ for (let param of this.paramMap.keys()) >+ params.push(param.name); >+ return `// ${this._func.name}(${params.join(", ")}) @ ${this._func.origin.originString}\n`; >+ } >+ >+ toString() >+ { >+ let declLine = ""; >+ >+ if (this.isShader) >+ declLine += `${this._func.shaderType} `; >+ declLine += `${this._typeUnifier.uniqueTypeId(this._func.returnType)} `; >+ declLine += this._funcMangler.mangle(this.func); >+ declLine += "(" >+ >+ let params = []; >+ const paramMap = this.paramMap; >+ for (let param of this._func.parameters) { >+ let pStr = `${this._typeUnifier.uniqueTypeId(param.type)} ${paramMap.get(param)}`; >+ // FIXME: The parser doesn't currently support vertex shaders having uint parameters, so this doesn't work. >+ if (this.parameterIsVertexId(param) && this.isVertexShader) >+ pStr += " [[vertex_id]]"; >+ else if (this.parameterIsAttribute(param)) >+ pStr += " [[stage_in]]"; >+ params.push(pStr); >+ } >+ >+ declLine += params.join(", ") + ")"; >+ >+ return declLine; >+ } >+ >+ get isShader() >+ { >+ // FIXME: Support WHLSL "compute" shaders (MSL calls these kernel shaders) >+ return this.func.shaderType === "vertex" || this.func.shaderType === "fragment"; >+ } >+ >+ parameterIsAttribute(node) >+ { >+ // We currently assuming that all parameters to entry points are attributes. >+ // TODO: Better logic for this, i.e. support samplers. >+ return this.isShader; >+ } >+ >+ parameterIsVertexId(node) >+ { >+ // FIXME: This isn't final, and isn't formally specified yet. >+ return this.isVertexShader && node.name == "wsl_vertexID" && node.type.name == "uint32"; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.js >new file mode 100644 >index 0000000000000000000000000000000000000000..d1f30f29ddc01cb7a631e3903fdeb7f210806527 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.js >@@ -0,0 +1,37 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+class MSLFunctionDefinition extends MSLFunctionDeclaration >+{ >+ toString() >+ { >+ let src = this.commentLine() + "\n" + super.toString(); >+ src += "\n{\n"; >+ let emitter = new MSLStatementEmitter(this.funcMangler, this.typeUnifier, this.func, this.paramMap, this.func.name, this.typeAttributes); >+ src += emitter.indentedSource(); >+ src += "}"; >+ return src; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.js >new file mode 100644 >index 0000000000000000000000000000000000000000..912544c0dae1ed7dfe2f9ec1eb85189d4cb91ff5 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.js >@@ -0,0 +1,32 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+class MSLFunctionForwardDeclaration extends MSLFunctionDeclaration >+{ >+ toString() >+ { >+ return this.commentLine() + "\n" + super.toString() + ";" >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLNameMangler.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLNameMangler.js >new file mode 100644 >index 0000000000000000000000000000000000000000..49a30df91f821748132329629e5b55a520206bb1 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLNameMangler.js >@@ -0,0 +1,41 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+class MSLNameMangler { >+ >+ constructor(prefix) >+ { >+ this._prefix = prefix; >+ this._counter = 0; >+ this._mangledNameMap = new Map(); >+ } >+ >+ mangle(key) >+ { >+ if (!this._mangledNameMap.has(key)) >+ this._mangledNameMap.set(key, `${this._prefix}${this._counter++}`); >+ return this._mangledNameMap.get(key); >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLNativeFunctionCall.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLNativeFunctionCall.js >new file mode 100644 >index 0000000000000000000000000000000000000000..2ab8717fcd1ad70757fbf0a879cec91225813ccd >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLNativeFunctionCall.js >@@ -0,0 +1,82 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+"use strict"; >+ >+function mslNativeFunctionCall(node, resultVariable, args) >+{ >+ const key = node.toString(); >+ >+ // FIXME: Implement the sampling functions. >+ // FIXME: Implement functions like f16tof32, asfloat, etc. >+ // FIXME: Implement tests for all native functions https://bugs.webkit.org/show_bug.cgi?id=189535. >+ const functionsWithTheSameCallingConvention = { >+ "native bool isfinite(float)" : "isfinite", >+ "native bool isinf(float)" : "isinf", >+ "native bool isnormal(float)" : "isnormal", >+ "native bool isnormal(half)" : "isnormal", >+ "native float acos(float)" : "acos", >+ "native float asfloat(int)" : "static_cast<float>", >+ "native float asfloat(uint)" : "static_cast<float>", >+ "native int asint(float)" : "static_cast<int>", >+ "native uint asuint(float)" : "static_cast<uint>", >+ "native float asin(float)" : "asin", >+ "native float atan(float)" : "atan", >+ "native float atan2(float,float)" : "atan2", >+ "native float ceil(float)" : "ceil", >+ "native float cos(float)" : "cos", >+ "native float cosh(float)" : "cosh", >+ "native float ddx(float)" : "dfdx", >+ "native float ddy(float)" : "dfdy", >+ "native float exp(float)" : "exp", >+ "native float floor(float)" : "floor", >+ "native float log(float)" : "log", >+ "native float pow(float,float)" : "pow", >+ "native float round(float)" : "round", >+ "native float sin(float)" : "sin", >+ "native float sinh(float)" : "sinh", >+ "native float sqrt(float)" : "sqrt", >+ "native float tan(float)" : "tan", >+ "native float tanh(float)" : "tanh", >+ "native float trunc(float)" : "trunc", >+ }; >+ >+ if (key in functionsWithTheSameCallingConvention) { >+ const callString = `${functionsWithTheSameCallingConvention[key]}(${args.join(", ")})`; >+ if (resultVariable) >+ return `${resultVariable} = ${callString};`; >+ else >+ return `${callString};`; >+ } >+ >+ const functionsWithDifferentCallingConventions = { >+ "native uint f32tof16(float)" : () => `${resultVariable} = uint(static_cast<ushort>(half(${args[0]})));`, >+ "native float f16tof32(uint)" : () => `${resultVariable} = float(static_cast<half>(ushort(${args[0]})));` >+ }; >+ if (key in functionsWithDifferentCallingConventions) >+ return functionsWithDifferentCallingConventions[key](); >+ >+ throw new Error(`${node} doesn't have mapping to a native Metal function.`); >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.js >new file mode 100644 >index 0000000000000000000000000000000000000000..a105016cd2c343abb893b653aa1a9511753a7e65 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.js >@@ -0,0 +1,796 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+class MSLStatementEmitter extends Visitor { >+ >+ constructor(funcMangler, typeUnifier, func, paramMap, debugName, typeAttributes) >+ { >+ super(); >+ this._funcMangler = funcMangler; >+ this._typeUnifier = typeUnifier; >+ this._func = func; >+ this._nameMap = paramMap; >+ this._debugName = debugName; >+ this._typeAttributes = typeAttributes; >+ this._counter = 0; >+ this._indentLevel = 0; >+ this._lines = []; >+ >+ this._loopConditionVariableStack = []; >+ this._loopConditionNodeStack = []; >+ this._loopIncrementNodeStack = []; >+ >+ this._actualResult = this._func.visit(this); >+ } >+ >+ _emitTrap() >+ { >+ this._add('// FIXME: Handle traps.'); >+ } >+ >+ _zeroInitialize(type, variableName, allowComment = true) >+ { >+ const emitter = this; >+ >+ class ZeroInitializer extends Visitor { >+ visitNativeType(node) >+ { >+ if (node.name == "bool") >+ emitter._add(`${variableName} = false;`); >+ else >+ emitter._add(`${variableName} = 0;`); >+ } >+ >+ visitPtrType(node) >+ { >+ emitter._add(`${variableName} = nullptr;`); >+ } >+ >+ visitArrayType(node) >+ { >+ for (let i = 0; i < node.numElements.value; i++) >+ emitter._zeroInitialize(node.elementType, `${variableName}[${i}]`, false); >+ } >+ >+ visitArrayRefType(node) >+ { >+ emitter._add(`${variableName}.ptr = nullptr;`); >+ emitter._add(`${variableName}.length = 0;`); >+ } >+ >+ visitEnumType(node) >+ { >+ emitter._zeroInitialize(node.baseType, variableName, false); >+ } >+ >+ visitStructType(node) >+ { >+ const typeAttributes = emitter._typeAttributes.attributesForType(node); >+ >+ for (let field of node.fields) { >+ const fieldName = typeAttributes.mangledFieldName(field.name); >+ emitter._zeroInitialize(field.type, `${variableName}.${fieldName}`, false); >+ } >+ } >+ >+ visitVectorType(node) >+ { >+ const elementNames = [ "x", "y", "z", "w" ]; >+ for (let i = 0; i < node.numElementsValue; i++) >+ emitter._add(`${variableName}.${elementNames[i]} = 0;`); >+ } >+ >+ visitMatrixType(node) >+ { >+ for (let i = 0; i < node.numRowsValue; i++) { >+ for (let j = 0; j < node.numRowsValue; j++) >+ emitter._zeroInitialize(node.elementType, `${variableName}[${i}][${j}]`, false); >+ } >+ } >+ >+ visitTypeRef(node) >+ { >+ node.type.visit(this); >+ } >+ } >+ if (allowComment) >+ this._add(`// Zero initialization of ${variableName}`); >+ Node.visit(type, new ZeroInitializer()); >+ } >+ >+ get actualResult() >+ { >+ return this._actualResult; >+ } >+ >+ get lines() >+ { >+ return this._lines; >+ } >+ >+ indentedSource() >+ { >+ return this._lines.map(line => " " + line + "\n").join(""); >+ } >+ >+ get _loopConditionVariable() >+ { >+ return this._loopConditionVariableStack[this._loopConditionVariableStack.length - 1]; >+ } >+ >+ get _loopCondition() >+ { >+ return this._loopConditionNodeStack[this._loopConditionNodeStack.length - 1]; >+ } >+ >+ get _loopIncrement() >+ { >+ return this._loopIncrementNodeStack[this._loopIncrementNodeStack.length - 1]; >+ } >+ >+ _add(linesString) >+ { >+ for (let line of linesString.split('\n')) { >+ for (let i = 0; i < this._indentLevel; i++) >+ line = " " + line; >+ this._lines.push(line); >+ } >+ } >+ >+ _addLines(lines) >+ { >+ for (let line of lines) >+ this._add(line); >+ } >+ >+ _fresh() >+ { >+ return `V${this._counter++}`; >+ } >+ >+ _indent(closure) >+ { >+ this._indentLevel++; >+ const result = closure(); >+ this._indentLevel--; >+ return result; >+ } >+ >+ // Atomic statements. >+ >+ visitBreak(node) >+ { >+ this._add("break;") >+ } >+ >+ visitContinue(node) >+ { >+ // This is necessary because for loops are compiled as while loops, so we need to do >+ // the loop increment and condition check before returning to the beginning of the loop. >+ this._emitLoopBodyEnd(); >+ this._add("continue;"); >+ } >+ >+ // Basic compound statements. >+ >+ visitBlock(block) >+ { >+ for (let statement of block.statements) { >+ this._add(`// ${this._debugName} @ ${statement.origin.originString}`); >+ statement.visit(this); >+ } >+ } >+ >+ visitReturn(node) >+ { >+ if (node.value) >+ this._add(`return ${node.value.visit(this)};`); >+ else >+ this._add(`return;`); >+ } >+ >+ _visitAndIndent(node) >+ { >+ return this._indent(() => node.visit(this)); >+ } >+ >+ visitIfStatement(node) >+ { >+ let condition = node.conditional.visit(this); >+ this._add(`if (${condition}) {`); >+ this._visitAndIndent(node.body); >+ if (node.elseBody) { >+ this._add(`} else {`); >+ this._visitAndIndent(node.elseBody); >+ this._add('}'); >+ } else >+ this._add('}'); >+ } >+ >+ visitIdentityExpression(node) >+ { >+ return node.target.visit(this); >+ } >+ >+ visitLogicalNot(node) >+ { >+ const type = typeOf(node); >+ let expr = Node.visit(node.operand, this); >+ let id = this._fresh(); >+ this._add(`${this._typeUnifier.uniqueTypeId(type)} ${id};`); >+ this._add(`${id} = !(${expr});`); >+ return id; >+ } >+ >+ visitDereferenceExpression(node) >+ { >+ const ptr = node.ptr.visit(this); >+ this._add(`if (!${ptr}) {`); >+ this._indent(() => this._emitTrap()); >+ this._add('}'); >+ const id = this._fresh(); >+ this._add(`${this._typeUnifier.uniqueTypeId(node.type)} ${id};`); >+ this._emitMemCopy(node.type, id, `(*${ptr})`); >+ return id; >+ } >+ >+ _isOperatorAnder(node) >+ { >+ const anderRegex = /^operator\&\.(.*?)$/; >+ return node instanceof NativeFunc && anderRegex.test(node.name); >+ } >+ >+ _isOperatorGetter(node) >+ { >+ const getterRegex = /^operator\.(.*?)$/; >+ return node instanceof NativeFunc && getterRegex.test(node.name); >+ } >+ >+ _isOperatorIndexer(node) >+ { >+ return node instanceof NativeFunc && node.name == "operator&[]"; >+ } >+ >+ _isOperatorSetter(node) >+ { >+ const setterRegex = /^operator\.(.*?)=$/; >+ return node instanceof NativeFunc && setterRegex.test(node.name) >+ } >+ >+ _isOperatorCast(node) >+ { >+ return node instanceof NativeFunc && node.name == "operator cast"; >+ } >+ >+ _isUnaryOperator(node) >+ { >+ const operatorRegex = /^operator\~$/; >+ return node instanceof NativeFunc && operatorRegex.test(node.name); >+ } >+ >+ _isBinaryOperator(node) >+ { >+ const operatorRegex = /operator(\+|\-|\*|\/|\^|\&|\||\&\&|\|\||\<\<|\>\>|\<|\<\=|\>|\>\=|\=\=|\!\=)$/; >+ return node instanceof NativeFunc && operatorRegex.test(node.name); >+ } >+ >+ _isOperatorValue(node) >+ { >+ return node instanceof NativeFunc && node.name == "operator.value"; >+ } >+ >+ _isOperatorLength(node) >+ { >+ return node instanceof NativeFunc && node.name == "operator.length"; >+ } >+ >+ _extractOperatorName(node) >+ { >+ return node.name.substring("operator".length); >+ } >+ >+ visitVariableDecl(node) >+ { >+ let id = this._fresh(); >+ this._nameMap.set(node, id); >+ >+ this._add(`${this._typeUnifier.uniqueTypeId(node.type)} ${id}; // ${node}`); >+ >+ if (node.initializer) { >+ let expr = node.initializer.visit(this); >+ this._emitMemCopy(typeOf(node), id, expr); >+ } else >+ this._zeroInitialize(node.type, id); >+ } >+ >+ visitVariableRef(node) >+ { >+ if (!this._nameMap.has(node.variable)) >+ throw new Error(`${node.variable} not found in this function's (${this._func.name}) variable map`); >+ return this._nameMap.get(node.variable); >+ } >+ >+ visitMakeArrayRefExpression(node) >+ { >+ const elemName = this._emitAsLValue(node.lValue); >+ const arrayType = typeOf(node.lValue); >+ const id = this._fresh(); >+ this._add(`${this._typeUnifier.uniqueTypeId(node.type)} ${id};`); >+ this._add(`${id}.length = ${node.numElements.value};`); >+ if (arrayType.isArray) >+ this._add(`${id}.ptr = ${elemName};`); >+ else >+ this._add(`${id}.ptr = &(${elemName});`); >+ return id; >+ } >+ >+ visitConvertPtrToArrayRefExpression(node) >+ { >+ const lValue = this._emitAsLValue(node.lValue); >+ const type = typeOf(node); >+ const id = this._fresh(); >+ this._add(`${this._typeUnifier.uniqueTypeId(type)} ${id};`); >+ this._add(`${id}.length = 1;`); >+ this._add(`${id}.ptr = ${lValue};`); >+ return id; >+ } >+ >+ visitAnonymousVariable(node) >+ { >+ let id = this._fresh(); >+ this._nameMap.set(node, id); >+ this._add(`${this._typeUnifier.uniqueTypeId(node.type)} ${id}; // ${node.name}`); >+ this._zeroInitialize(node.type, id); >+ return id; >+ } >+ >+ visitAssignment(node) >+ { >+ const lhs = this._emitAsLValue(node.lhs); >+ const rhs = node.rhs.visit(this); >+ this._emitMemCopy(typeOf(node), lhs, rhs); >+ return lhs; >+ } >+ >+ _emitMemCopy(type, dest, src) >+ { >+ type = typeOf(type); >+ if (type instanceof ArrayType) { >+ for (let i = 0; i < type.numElementsValue; i++) >+ this._emitMemCopy(typeOf(type.elementType), `${dest}[${i}]`, `${src}[${i}]`); >+ } else { >+ this._add(`// ${type}`); >+ this._add(`${dest} = ${src};`); >+ } >+ } >+ >+ visitCommaExpression(node) >+ { >+ let result; >+ for (let expr of node.list) >+ result = Node.visit(expr, this); >+ return result; >+ } >+ >+ visitCallExpression(node) >+ { >+ const args = []; >+ for (let i = node.argumentList.length; i--;) >+ args.unshift(node.argumentList[i].visit(this)); >+ >+ let resultVariable; >+ if (node.func.returnType.name !== "void") { >+ resultVariable = this._fresh(); >+ this._add(`// Result variable for call ${node.func.name}(${args.join(", ")})`); >+ this._add(`${this._typeUnifier.uniqueTypeId(node.resultType)} ${resultVariable};`); >+ } >+ >+ if (node.func instanceof FuncDef) { >+ const mangledCallName = this._funcMangler.mangle(node.func); >+ const callString = `${mangledCallName}(${args.join(", ")})`; >+ if (resultVariable) >+ this._add(`${resultVariable} = ${callString};`); >+ else >+ this._add(`${callString};`); >+ } else >+ this._makeNativeFunctionCall(node.func, resultVariable, args); >+ >+ return resultVariable; >+ } >+ >+ _makeNativeFunctionCall(node, resultVariable, args) >+ { >+ if (!(node instanceof NativeFunc)) >+ throw new Error(`${node} should be a native function.`); >+ >+ if (this._isOperatorAnder(node)) >+ this._emitOperatorAnder(node, resultVariable, args); >+ else if (node.implementationData instanceof BuiltinVectorGetter) >+ this._emitBuiltinVectorGetter(node, resultVariable, args); >+ else if (node.implementationData instanceof BuiltinVectorSetter) >+ this._emitBuiltinVectorSetter(node, resultVariable, args); >+ else if (node.implementationData instanceof BuiltinMatrixGetter) >+ this._emitBuiltinMatrixGetter(node, resultVariable, args); >+ else if (node.implementationData instanceof BuiltinMatrixSetter) >+ this._emitBuiltinMatrixSetter(node, resultVariable, args); >+ else if (this._isOperatorValue(node)) >+ this._add(`${resultVariable} = ${args[0]};`); >+ else if (this._isOperatorLength(node)) >+ this._emitOperatorLength(node, resultVariable, args); >+ else if (this._isOperatorSetter(node)) >+ this._emitOperatorSetter(node, resultVariable, args); >+ else if (this._isOperatorGetter(node)) >+ this._emitOperatorGetter(node, resultVariable, args); >+ else if (this._isOperatorIndexer(node)) >+ this._emitOperatorIndexer(node, resultVariable, args); >+ else if (this._isOperatorCast(node)) >+ this._emitOperatorCast(node, resultVariable, args); >+ else if (this._isUnaryOperator(node)) >+ this._add(`${resultVariable} = ${this._extractOperatorName(node)}(${args[0]});`); >+ else if (this._isBinaryOperator(node)) >+ this._add(`${resultVariable} = ${args[0]} ${this._extractOperatorName(node)} ${args[1]};`); >+ else >+ this._add(mslNativeFunctionCall(node, resultVariable, args)); >+ >+ return resultVariable; >+ } >+ >+ _emitCallToMetalFunction(name, result, args) >+ { >+ this._add(`${result} = ${name}(${args.join(", ")});`); >+ } >+ >+ _emitBuiltinVectorGetter(node, result, args) >+ { >+ this._add(`${result} = ${args[0]}.${node.implementationData.elementName};`); >+ } >+ >+ _emitBuiltinVectorSetter(node, result, args) >+ { >+ this._add(`${result} = ${args[0]};`); >+ this._add(`${result}.${node.implementationData.elementName} = ${args[1]};`); >+ } >+ >+ _emitBuiltinMatrixGetter(node, result, args) >+ { >+ this._add(`if (${args[1]} >= ${node.implementationData.height}) {`); >+ this._indent(() => this._emitTrap()); >+ this._add('}'); >+ this._add(`${result} = ${args[0]}[${args[1]}];`); >+ } >+ >+ _emitBuiltinMatrixSetter(node, result, args) >+ { >+ this._add(`if (${args[1]} >= ${node.implementationData.height}) {`); >+ this._indent(() => this._emitTrap()); >+ this._add('}'); >+ this._add(`${result} = ${args[0]};`); >+ this._add(`${result}[${args[1]}] = ${args[2]};`); >+ } >+ >+ _emitOperatorLength(node, result, args) >+ { >+ const paramType = typeOf(node.parameters[0]); >+ if (paramType instanceof ArrayRefType) >+ this._add(`${result} = ${args[0]}.length;`); >+ else if (paramType instanceof ArrayType) >+ this._add(`${result} = ${paramType.numElements.visit(this)};`); >+ else >+ throw new Error(`Unhandled paramter type ${paramType} for operator.length`); >+ } >+ >+ _emitOperatorCast(node, result, args) >+ { >+ const retType = this._typeUnifier.uniqueTypeId(node.returnType); >+ this._add(`${result} = ${retType}(${args.join(", ")});`); >+ } >+ >+ _emitOperatorIndexer(node, result, args) >+ { >+ if (!(typeOf(node.parameters[0]) instanceof ArrayRefType)) >+ throw new Error(`Support for operator&[] ${node.parameters[0]} not implemented.`); >+ >+ this._add(`if (${args[1]} >= ${args[0]}.length) {`); >+ this._indent(() => this._emitTrap()); >+ this._add(`}`); >+ this._add(`// Operator indexer`); >+ this._add(`${result} = &(${args[0]}.ptr[${args[1]}]);`); >+ } >+ >+ _emitOperatorAnder(node, result, args) >+ { >+ const type = node.implementationData.structType; >+ const field = this._typeAttributes.attributesForType(type).mangledFieldName(node.implementationData.name); >+ this._add(`${result} = &((${args[0]})->${field});`); >+ } >+ >+ _emitOperatorGetter(node, result, args) >+ { >+ const type = node.implementationData.type; >+ const field = this._typeAttributes.attributesForType(type).mangledFieldName(node.implementationData.name); >+ this._add(`${result} = ${args[0]}.${field};`); >+ } >+ >+ _emitOperatorSetter(node, result, args) >+ { >+ // FIXME: Currently no WHLSL source produces structs that only have getters/setters rather than the ander, >+ // and the ander is always preferred over the getter and setter. Therefore this code is untested. >+ const type = node.implementationData.type; >+ const field = this._typeAttributes.attributesForType(type).mangledFieldName(node.implementationData.name); >+ this._add(`${args[0]}.${field} = ${args[1]};`); >+ this._add(`${result} = ${args[0]}`); >+ } >+ >+ visitMakePtrExpression(node) >+ { >+ if (node.target instanceof DereferenceExpression) >+ return this._emitAsLValue(node.target.ptr); >+ return `&(${this._emitAsLValue(node.lValue)})`; >+ } >+ >+ // Loop code generation. Loops all follow the same style where they declare a conditional variable (boolean) >+ // which is checked on each iteration (all loops are compiled to a while loop or do/while loop). The check is >+ // emitted in _emitLoopCondition. For loops additionally have _emitLoopBodyEnd for the increment on each iteration. >+ >+ visitForLoop(node) >+ { >+ this._emitAsLoop(node.condition, node.increment, (conditionVar) => { >+ Node.visit(node.initialization, this); >+ this._emitLoopCondition(); >+ this._add(`while (${conditionVar}) {`); >+ this._indent(() => { >+ node.body.visit(this); >+ this._emitLoopBodyEnd(); >+ }); >+ this._add('}'); >+ }); >+ } >+ >+ visitWhileLoop(node) >+ { >+ this._emitAsLoop(node.conditional, null, (conditionVar) => { >+ this._add(`while (${conditionVar}) {`); >+ this._indent(() => { >+ node.body.visit(this); >+ this._emitLoopCondition(); >+ }); >+ this._add('}'); >+ }); >+ } >+ >+ visitDoWhileLoop(node) >+ { >+ this._emitAsLoop(node.conditional, null, conditionVar => { >+ this._add("do {"); >+ this._indent(() => { >+ node.body.visit(this); >+ this._emitLoopBodyEnd(); >+ }); >+ this._add(`} while (${conditionVar});`); >+ }); >+ } >+ >+ _emitAsLoop(conditional, increment, emitLoopBody) >+ { >+ const conditionVar = this._fresh(); >+ this._loopConditionVariableStack.push(conditionVar); >+ this._loopConditionNodeStack.push(conditional); >+ this._loopIncrementNodeStack.push(increment); >+ this._add(`bool ${conditionVar} = true;`); >+ >+ emitLoopBody(conditionVar); >+ >+ this._loopIncrementNodeStack.pop(); >+ this._loopConditionVariableStack.pop(); >+ this._loopConditionNodeStack.pop(); >+ } >+ >+ _emitLoopBodyEnd() >+ { >+ this._emitLoopIncrement(); >+ this._emitLoopCondition(); >+ } >+ >+ _emitLoopIncrement() >+ { >+ Node.visit(this._loopIncrement, this); >+ } >+ >+ _emitLoopCondition() >+ { >+ if (this._loopCondition) { >+ const conditionResult = this._loopCondition.visit(this); >+ this._add(`${this._loopConditionVariable} = ${conditionResult};`); >+ } >+ } >+ >+ visitSwitchStatement(node) >+ { >+ const caseValueEmitter = new MSLConstexprEmitter(); >+ >+ let switchValue = Node.visit(node.value, this); >+ this._add(`switch (${switchValue}) {`); >+ this._indent(() => { >+ for (let i = 0; i < node.switchCases.length; i++) { >+ let switchCase = node.switchCases[i]; >+ if (!switchCase.isDefault) >+ this._add(`case ${switchCase.value.visit(caseValueEmitter)}: {`); >+ else >+ this._add("default: {"); >+ this._visitAndIndent(switchCase.body); >+ this._add("}"); >+ } >+ }); >+ this._add("}"); >+ } >+ >+ visitSwitchCase(node) >+ { >+ throw new Error(`MSLStatementEmitter.visitSwitchCase called; switch statements should be fully handled by MSLStatementEmitter.visitSwitchStatement.`); >+ } >+ >+ visitBoolLiteral(node) >+ { >+ const fresh = this._fresh(); >+ this._add(`bool ${fresh} = ${node.value};`); >+ return fresh; >+ } >+ >+ visitEnumLiteral(node) >+ { >+ return node.member.value.visit(this); >+ } >+ >+ visitGenericLiteral(node) >+ { >+ const fresh = this._fresh(); >+ this._add(`${this._typeUnifier.uniqueTypeId(node.type)} ${fresh};`); >+ this._add(`${fresh} = ${node.value};`); >+ return fresh; >+ } >+ >+ visitNullLiteral(node) >+ { >+ return `nullptr`; >+ } >+ >+ visitDotExpression(node) >+ { >+ throw new Error(`MSLStatementEmitter.visitDotExpression called; ${node} should have been converted to an operator function call.`); >+ } >+ >+ visitTrapStatement(node) >+ { >+ this._emitTrap(); >+ } >+ >+ visitLogicalExpression(node) >+ { >+ const result = this._fresh(); >+ this._add(`// Result variable for ${node.text}`); >+ this._add(`bool ${result};`); >+ if (node.text == "&&") { >+ const left = node.left.visit(this); >+ this._add(`if (!${left}) {`) >+ this._indent(() => this._add(`${result} = false;`)); >+ this._add('} else {'); >+ this._indent(() => this._add(`${result} = ${node.right.visit(this)};`)); >+ this._add('}'); >+ } else if (node.text == "||") { >+ const left = node.left.visit(this); >+ this._add(`${result} = ${left};`); >+ this._add(`if (!${result}) {`); >+ this._indent(() => this._add(`${result} = ${node.right.visit(this)};`)); >+ this._add("}"); >+ } else >+ throw new Error(`Unrecognized logical expression ${node.text}`); >+ return result; >+ } >+ >+ visitTernaryExpression(node) >+ { >+ // FIXME: If each of the three expressions isn't a compound expression or statement then just use ternary syntax. >+ let resultType = typeOf(node.bodyExpression); >+ let resultVar = this._fresh(); >+ this._add(`${this._typeUnifier.uniqueTypeId(resultType)} ${resultVar};`); >+ const predicate = node.predicate.visit(this); >+ this._add(`if (${predicate}) {`); >+ this._indent(() => { >+ const bodyExpression = node.bodyExpression.visit(this); >+ this._emitMemCopy(resultType, resultVar, bodyExpression); >+ }); >+ this._add("} else {"); >+ this._indent(() => { >+ const elseExpression = node.elseExpression.visit(this); >+ this._emitMemCopy(resultType, resultVar, elseExpression); >+ }); >+ this._add("}"); >+ >+ return resultVar; >+ } >+ >+ visitIndexExpression(node) >+ { >+ throw new Error("MSLStatementEmitter.visitIndexExpression is not implemented; IndexExpression should not appear in the AST for Metal codegen."); >+ } >+ >+ visitNativeFunc(node) >+ { >+ throw new Error("MSLStatementEmitter.visitNativeFunc is not implemented; NativeFunction is not compiled by the Metal codegen."); >+ } >+ >+ visitNativeTypeInstance(node) >+ { >+ throw new Error("MSLStatementEmitter.visitNativeTypeInstance is not implemented."); >+ } >+ >+ visitReadModifyWriteExpression(node) >+ { >+ throw new Error("MSLStatementEmitter.visitReadModifyWriteExpression is not implemented; it should have been transformed out the tree in earlier stage."); >+ } >+ >+ _emitAsLValue(node) >+ { >+ const lOrRValueEmitter = this; >+ class LValueVisitor extends Visitor >+ { >+ visitDereferenceExpression(node) >+ { >+ const target = node.ptr.visit(lOrRValueEmitter); >+ lOrRValueEmitter._add(`if (!(${target})) {`); >+ lOrRValueEmitter._indent(() => lOrRValueEmitter._emitTrap()); >+ lOrRValueEmitter._add("}"); >+ return `(*(${target}))`; >+ } >+ >+ visitVariableRef(node) >+ { >+ return node.visit(lOrRValueEmitter); >+ } >+ >+ visitIdentityExpression(node) >+ { >+ return node.target.visit(this); >+ } >+ >+ visitCallExpression(node) >+ { >+ // FIXME: Is this comment actually true??? >+ // Only occurs for @(...) expressions. >+ return node.visit(lOrRValueEmitter); >+ } >+ >+ visitMakePtrExpression(node) >+ { >+ if (node.lValue instanceof DereferenceExpression) >+ return node.lValue.ptr.visit(lOrRValueEmitter); >+ } >+ } >+ const result = node.visit(new LValueVisitor()); >+ if (!result) >+ throw new Error(`${node} not a valid l-value.`); >+ return result; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeAttributes.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeAttributes.js >new file mode 100644 >index 0000000000000000000000000000000000000000..d6f1676130a8d03fb2f757b9c2b7ed8c41ca1750 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeAttributes.js >@@ -0,0 +1,86 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+class MSLTypeAttributes { >+ >+ constructor(type) >+ { >+ this._type = type; >+ this._isVertexAttribute = false; >+ this._isVertexOutputOrFragmentInput = false; >+ this._isFragmentOutput = false; >+ this._fieldMangler = new MSLNameMangler('field'); >+ >+ if (type instanceof StructType) { >+ for (let field of type.fields) >+ this._fieldMangler.mangle(field.name) >+ } >+ } >+ >+ get type() >+ { >+ return this._type; >+ } >+ >+ get isVertexAttribute() >+ { >+ return this._isVertexAttribute; >+ } >+ >+ set isVertexAttribute(va) >+ { >+ this._isVertexAttribute = va; >+ } >+ >+ get isVertexOutputOrFragmentInput() >+ { >+ return this._isVertexOutputOrFragmentInput; >+ } >+ >+ set isVertexOutputOrFragmentInput(vo) >+ { >+ this._isVertexOutputOrFragmentInput = vo; >+ } >+ >+ get isFragmentOutput() >+ { >+ return this._isFragmentOutput; >+ } >+ >+ set isFragmentOutput(fo) >+ { >+ this._isFragmentOutput = fo; >+ } >+ >+ get fieldMangler() >+ { >+ return this._fieldMangler; >+ } >+ >+ mangledFieldName(fieldName) >+ { >+ return this.fieldMangler.mangle(fieldName); >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeAttributesMap.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeAttributesMap.js >new file mode 100644 >index 0000000000000000000000000000000000000000..373b0e8c0fe93488988b6f830c7a9c6f703a28f6 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeAttributesMap.js >@@ -0,0 +1,66 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+// Provides lookup for all the top level types in the program. >+class MSLTypeAttributesMap { >+ >+ constructor(functionDefs, typeUnifier) >+ { >+ this._typeUnifier = typeUnifier; >+ this._typeAttributeMap = new Map(); >+ >+ for (let funcDef of functionDefs) { >+ if (funcDef.shaderType == "vertex") >+ this._visitVertexShader(funcDef); >+ else if (funcDef.shaderType == "fragment") >+ this._visitFragmentShader(funcDef); >+ } >+ } >+ >+ attributesForType(type) >+ { >+ const key = this._typeUnifier.uniqueTypeId(type); >+ let attrs = this._typeAttributeMap.get(key); >+ if (!attrs) >+ this._typeAttributeMap.set(key, attrs = new MSLTypeAttributes(type)); >+ return attrs; >+ } >+ >+ _visitVertexShader(func) >+ { >+ this.attributesForType(func.returnType).isVertexOutputOrFragmentInput = true; >+ for (let param of func.parameters) >+ this.attributesForType(param.type).isVertexAttribute = true; >+ } >+ >+ _visitFragmentShader(func) >+ { >+ this.attributesForType(func.returnType).isFragmentOutput = true; >+ for (let param of func.parameters) >+ this.attributesForType(param.type).isVertexOutputOrFragmentInput = true; >+ } >+ >+ // FIXME: Support compute shaders. >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeUnifier.js b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeUnifier.js >new file mode 100644 >index 0000000000000000000000000000000000000000..029257559a833febb0b6aea58936bc6085928a3b >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeUnifier.js >@@ -0,0 +1,159 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+ >+class MSLTypeUnifier extends Visitor { >+ >+ constructor() >+ { >+ super(); >+ >+ this._typeNameMangler = new MSLNameMangler('T'); >+ this._allTypes = new Set(); >+ } >+ >+ get allTypes() >+ { >+ return this._allTypes; >+ } >+ >+ uniqueTypeId(type) >+ { >+ const result = type.visit(this); >+ if (!result) >+ throw new Error(`${type} has no unique type ID.`); >+ return result; >+ } >+ >+ visitTypeRef(node) >+ { >+ if ((node.typeArguments && node.typeArguments.length) || !node.name) { >+ return node.type.visit(this); >+ } >+ >+ if (!this._allTypes.has(node.type)) >+ node.type.visit(this); >+ >+ const baseType = typeOf(node); >+ >+ if (baseType instanceof NativeType || baseType instanceof VectorType || baseType instanceof MatrixType || baseType instanceof EnumType || baseType instanceof ArrayType) >+ return baseType.visit(this); >+ >+ return this._typeNameMangler.mangle(node.name); >+ } >+ >+ visitStructType(node) >+ { >+ this._allTypes.add(node); >+ for (let field of node.fields) >+ field.visit(this); >+ node._typeID = this._typeNameMangler.mangle(node.name); >+ return node._typeID; >+ } >+ >+ visitEnumType(node) >+ { >+ this._allTypes.add(node); >+ node._typeID = node.baseType.visit(this); >+ return node._typeID; >+ } >+ >+ visitNativeType(node) >+ { >+ this._allTypes.add(node); >+ node._typeID = this._typeNameMangler.mangle(node.name); >+ return node._typeID; >+ } >+ >+ visitPtrType(node) >+ { >+ this._allTypes.add(node); >+ node.elementType.visit(this); >+ node._typeID = this._typeNameMangler.mangle(`${node.elementType.visit(this)}* ${node.addressSpace}`); >+ return node._typeID; >+ } >+ >+ visitArrayType(node) >+ { >+ this._allTypes.add(node); >+ node.elementType.visit(this); >+ node._typeID = this._typeNameMangler.mangle(`${node.elementType.visit(this)}[${node.numElements}]`); >+ return node._typeID; >+ } >+ >+ visitArrayRefType(node) >+ { >+ this._allTypes.add(node); >+ // The name mangler is used here because array refs are "user-defined" types in the sense >+ // that they will need to be defined as structs in the Metal output. >+ node._typeID = this._typeNameMangler.mangle(`${node.elementType.visit(this)}[] ${node.addressSpace}`); >+ return node._typeID; >+ } >+ >+ visitVectorType(node) >+ { >+ this._allTypes.add(node); >+ node._typeID = this._typeNameMangler.mangle(node.toString()); >+ return node._typeID; >+ } >+ >+ visitMatrixType(node) >+ { >+ this._allTypes.add(node); >+ node._typeID = this._typeNameMangler.mangle(node.toString()); >+ return node._typeID; >+ } >+ >+ visitMakeArrayRefExpression(node) >+ { >+ super.visitMakeArrayRefExpression(node); >+ node._typeID = node.type.visit(this); >+ // When the array ref is declared in MSL it will contained a pointer to the element type. However, the original >+ // WHLSL doesn't need to contain the type "pointer to the element type", so we ensure that the type is present >+ // here. The logic isn't needed for ConvertPtrToArrayRefExpression because the type is necesssarily present there. >+ new PtrType(node.origin, node.addressSpace ? node.addressSpace : "thread", typeOf(node).elementType).visit(this); >+ return node._typeID; >+ } >+ >+ visitGenericLiteralType(node) >+ { >+ node._typeID = node.type.visit(this); >+ return node._typeID; >+ } >+ >+ typesThatNeedDeclaration() >+ { >+ const declSet = new Set(); >+ const nameSet = new Set(); >+ for (let type of this._allTypes) { >+ const name = type.visit(this); >+ if (!nameSet.has(name)) { >+ declSet.add(type); >+ nameSet.add(name); >+ } >+ } >+ return declSet; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/TypeOf.js b/Tools/WebGPUShadingLanguageRI/Metal/TypeOf.js >new file mode 100644 >index 0000000000000000000000000000000000000000..7258ff3a624c8cffa5df6dda33aada25299ff316 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/TypeOf.js >@@ -0,0 +1,325 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+// FIXME: Rather than being a separate function this should instead happen in the Checker phase that annotates >+// each Node instance with a "type" property. https://bugs.webkit.org/show_bug.cgi?id=189611 >+function typeOf(node) { >+ class TypeVisitor extends Visitor { >+ visitAnonymousVariable(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitArrayRefType(node) >+ { >+ return node; >+ } >+ >+ visitArrayType(node) >+ { >+ return node; >+ } >+ >+ visitAssignment(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitCallExpression(node) >+ { >+ return node.func.visit(this); >+ } >+ >+ visitCommaExpression(node) >+ { >+ return node.list[node.list.length - 1].visit(this); >+ } >+ >+ visitConvertPtrToArrayRefExpression(node) >+ { >+ const ptrType = typeOf(node.lValue); >+ return new ArrayRefType(node.origin, ptrType.addressSpace, ptrType.elementType); >+ } >+ >+ visitDotExpression(node) >+ { >+ return node.struct.fieldMap.get(node.fieldName).type.visit(this); >+ } >+ >+ visitElementalType(node) >+ { >+ return node; >+ } >+ >+ visitEnumType(node) >+ { >+ return node; >+ } >+ >+ visitField(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitFunc(node) >+ { >+ return node.returnType.visit(this); >+ } >+ >+ visitFuncDef(node) >+ { >+ return node.returnType.visit(this); >+ } >+ >+ visitFuncParameter(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitFunctionLikeBlock(node) >+ { >+ return node.returnType.visit(this); >+ } >+ >+ visitGenericLiteralType(node) >+ { >+ return typeOf(node.type); >+ } >+ >+ visitIdentityExpression(node) >+ { >+ return node.target.visit(this); >+ } >+ >+ visitIndexExpression(node) >+ { >+ return node.array.elementType.visit(this); >+ } >+ >+ visitLogicalExpression(node) >+ { >+ throw new Error("FIXME: Implement TypeVisitor.visitLogicalExpression"); >+ } >+ >+ visitLogicalNot(node) >+ { >+ return node.operand.visit(this); >+ } >+ >+ visitMakeArrayRefExpression(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitMakePtrExpression(node) >+ { >+ return new PtrType(node.origin, "thread", typeOf(node.lValue)); >+ } >+ >+ visitMatrixType(node) >+ { >+ return node; >+ } >+ >+ visitNativeFunc(node) >+ { >+ return node.returnType.visit(this); >+ } >+ >+ visitNativeFuncInstance(node) >+ { >+ return node.returnType.visit(this); >+ } >+ >+ visitNativeType(node) >+ { >+ return node; >+ } >+ >+ visitNativeTypeInstance(node) >+ { >+ return node; >+ } >+ >+ visitNullLiteral(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitNullType(node) >+ { >+ return node; >+ } >+ >+ visitPtrType(node) >+ { >+ return node; >+ } >+ >+ visitReadModifyWriteExpression(node) >+ { >+ throw new Error("FIXME: Implement TypeVisitor.visitReadModifyWriteExpression"); >+ } >+ >+ visitReferenceType(node) >+ { >+ return node; >+ } >+ >+ visitStructType(node) >+ { >+ return node; >+ } >+ >+ visitTypeDef(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitTypeRef(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitVariableDecl(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitVariableRef(node) >+ { >+ return node.variable.type.visit(this); >+ } >+ >+ visitVectorType(node) >+ { >+ return node; >+ } >+ >+ visitBlock(node) >+ { >+ throw new Error("Block has no type"); >+ } >+ >+ visitBoolLiteral(node) >+ { >+ throw new Error("BoolLiteral has no type"); >+ } >+ >+ visitBreak(node) >+ { >+ throw new Error("Break has no type"); >+ } >+ >+ visitConstexprTypeParameter(node) >+ { >+ throw new Error("ConstexprTypeParameter has no type"); >+ } >+ >+ visitContinue(node) >+ { >+ throw new Error("Continue has no type"); >+ } >+ >+ visitDereferenceExpression(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ visitDoWhileLoop(node) >+ { >+ throw new Error("DoWhileLoop has no type"); >+ } >+ >+ visitEnumLiteral(node) >+ { >+ throw new Error("EnumLiteral has no type"); >+ } >+ >+ visitEnumMember(node) >+ { >+ throw new Error("EnumMember has no type"); >+ } >+ >+ visitForLoop(node) >+ { >+ throw new Error("ForLoop has no type"); >+ } >+ visitGenericLiteral(node) >+ { >+ return node.type.visit(this); >+ } >+ visitIfStatement(node) >+ { >+ throw new Error("IfStatement has no type"); >+ } >+ >+ visitProgram(node) >+ { >+ throw new Error("Program has no type"); >+ } >+ >+ visitProtocolDecl(node) >+ { >+ throw new Error("ProtocolDecl has no type"); >+ } >+ >+ visitProtocolFuncDecl(node) >+ { >+ throw new Error("ProtocolFuncDecl has no type"); >+ } >+ >+ visitProtocolRef(node) >+ { >+ throw new Error("ProtocolRef has no type"); >+ } >+ >+ visitReturn(node) >+ { >+ throw new Error("Return has no type"); >+ } >+ >+ visitSwitchCase(node) >+ { >+ throw new Error("SwitchCase has no type"); >+ } >+ >+ visitSwitchStatement(node) >+ { >+ throw new Error("SwitchStatement has no type"); >+ } >+ >+ visitTrapStatement(node) >+ { >+ throw new Error("TrapStatement has no type"); >+ } >+ >+ visitWhileLoop(node) >+ { >+ throw new Error("WhileLoop has no type"); >+ } >+ } >+ return node.visit(new TypeVisitor()); >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/WhlslToMsl.js b/Tools/WebGPUShadingLanguageRI/Metal/WhlslToMsl.js >new file mode 100644 >index 0000000000000000000000000000000000000000..5c85f2b49b2e3920a281f4e7278f21fc1ff4103f >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/WhlslToMsl.js >@@ -0,0 +1,41 @@ >+/* >+ * Copyright (C) 2018 Apple Inc. All rights reserved. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above copyright >+ * notice, this list of conditions and the following disclaimer in the >+ * documentation and/or other materials provided with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY >+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE >+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR >+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR >+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, >+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, >+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR >+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY >+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+// Main wrapper for the compiler. Clients should use this function to compile WHLSL. >+function whlslToMsl(src) >+{ >+ let parsedProgram; >+ try { >+ parsedProgram = prepare("/internal/test", 0, src); >+ } catch (e) { >+ return new MSLCompileResult(null, e, null, null); >+ } >+ >+ if (!(parsedProgram instanceof Program)) >+ return new MSLCompileResult(null, new Error("Compilation failed"), null, null); >+ >+ const compiler = new MSLBackend(parsedProgram); >+ return compiler.compile(); >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/PropertyResolver.js b/Tools/WebGPUShadingLanguageRI/PropertyResolver.js >index b5c454ac2dc3d534ca5213ef13c3736cd1700344..23184ca5ad2f3aab46416a56adfcbe084ef39b5c 100644 >--- a/Tools/WebGPUShadingLanguageRI/PropertyResolver.js >+++ b/Tools/WebGPUShadingLanguageRI/PropertyResolver.js >@@ -168,6 +168,8 @@ class PropertyResolver extends Visitor { > super.visitMakePtrExpression(node); > if (!node.lValue.isLValue) > throw new WTypeError(node.origin.originString, "Not an lvalue: " + node.lValue); >+ if (node.lValue.unifyNode instanceof DereferenceExpression) >+ node.become(new IdentityExpression(node.lValue.unifyNode.ptr)); > } > > visitMakeArrayRefExpression(node) >diff --git a/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js b/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js >index a0181468a25ed4c09050d7bf1d61f44ed3a0aa66..53ab6b5442483574263c1dc689161add2685ecab 100644 >--- a/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js >+++ b/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js >@@ -39,9 +39,23 @@ function synthesizeStructAccessors(program) > implementationData.type.visit(visitor); > }; > >+ nativeFunc.visitImplementationData = (implementationData, visitor) => { >+ // Visiting the type first ensures that the struct layout builder figures out the field's >+ // offset. >+ implementationData.type.visit(visitor); >+ }; >+ > nativeFunc.implementation = (argumentList, node) => { > return implementation(argumentList, field.offset, type.size, field.type.size); > }; >+ >+ nativeFunc.implementationData = { >+ structType: type, >+ name: field.name, >+ type: field.type, >+ offset: field.offset, >+ size: field.type.size >+ }; > } > > function createFieldType() >diff --git a/Tools/WebGPUShadingLanguageRI/Test.js b/Tools/WebGPUShadingLanguageRI/Test.js >index 5fd4bb3bbf93a55c21577e4507a1e5048d4bf566..aa0fb3112fdecde81a500809e4529385245ed787 100644 >--- a/Tools/WebGPUShadingLanguageRI/Test.js >+++ b/Tools/WebGPUShadingLanguageRI/Test.js >@@ -437,6 +437,27 @@ tests.intSimpleMath = function() { > checkInt(program, callFunction(program, "foo", [makeInt(program, 7), makeInt(program, -2)]), -3); > } > >+tests.incrementAndDecrement = function() { >+ let program = doPrep(` >+ test int foo1() { int x = 0; return x++; } >+ test int foo2() { int x = 0; x++; return x; } >+ test int foo3() { int x = 0; return ++x; } >+ test int foo4() { int x = 0; ++x; return x; } >+ test int foo5() { int x = 0; return x--; } >+ test int foo6() { int x = 0; x--; return x; } >+ test int foo7() { int x = 0; return --x; } >+ test int foo8() { int x = 0; --x; return x; } >+ `); >+ checkInt(program, callFunction(program, "foo1", []), 0); >+ checkInt(program, callFunction(program, "foo2", []), 1); >+ checkInt(program, callFunction(program, "foo3", []), 1); >+ checkInt(program, callFunction(program, "foo4", []), 1); >+ checkInt(program, callFunction(program, "foo5", []), 0); >+ checkInt(program, callFunction(program, "foo6", []), -1); >+ checkInt(program, callFunction(program, "foo7", []), -1); >+ checkInt(program, callFunction(program, "foo8", []), -1); >+} >+ > tests.uintSimpleMath = function() { > let program = doPrep("test uint foo(uint x, uint y) { return x + y; }"); > checkUint(program, callFunction(program, "foo", [makeUint(program, 7), makeUint(program, 5)]), 12); >@@ -2353,6 +2374,149 @@ tests.nestedSubscriptLValueEmulationSimple = function() > checkInt(program, callFunction(program, "testSetValuesMutateValuesAndSum", []), 5565); > } > >+tests.nestedSubscriptWithArraysInStructs = function() >+{ >+ let program = doPrep(` >+ struct Foo { >+ int[7] array; >+ } >+ int sum(Foo foo) >+ { >+ int result = 0; >+ for (uint i = 0; i < foo.array.length; i++) >+ result += foo.array[i]; >+ return result; >+ } >+ struct Bar { >+ Foo[6] array; >+ } >+ int sum(Bar bar) >+ { >+ int result = 0; >+ for (uint i = 0; i < bar.array.length; i++) >+ result += sum(bar.array[i]); >+ return result; >+ } >+ struct Baz { >+ Bar[5] array; >+ } >+ int sum(Baz baz) >+ { >+ int result = 0; >+ for (uint i = 0; i < baz.array.length; i++) >+ result += sum(baz.array[i]); >+ return result; >+ } >+ void setValues(thread Baz* baz) >+ { >+ for (uint i = 0; i < baz->array.length; i++) { >+ for (uint j = 0; j < baz->array[i].array.length; j++) { >+ for (uint k = 0; k < baz->array[i].array[j].array.length; k++) >+ baz->array[i].array[j].array[k] = int(i + j + k); >+ } >+ } >+ } >+ test int testSetValuesAndSum() >+ { >+ Baz baz; >+ setValues(&baz); >+ return sum(baz); >+ } >+ test int testSetValuesMutateValuesAndSum() >+ { >+ Baz baz; >+ setValues(&baz); >+ for (uint i = baz.array.length; i--;) { >+ for (uint j = baz.array[i].array.length; j--;) { >+ for (uint k = baz.array[i].array[j].array.length; k--;) >+ baz.array[i].array[j].array[k] = baz.array[i].array[j].array[k] * int(k); >+ } >+ } >+ return sum(baz); >+ } >+ `); >+ checkInt(program, callFunction(program, "testSetValuesAndSum", []), 1575); >+ checkInt(program, callFunction(program, "testSetValuesMutateValuesAndSum", []), 5565); >+} >+ >+tests.nestedSubscript = function() >+{ >+ let program = doPrep(` >+ int sum(int[7] array) >+ { >+ int result = 0; >+ for (uint i = array.length; i--;) >+ result += array[i]; >+ return result; >+ } >+ int sum(int[6][7] array) >+ { >+ int result = 0; >+ for (uint i = array.length; i--;) >+ result += sum(array[i]); >+ return result; >+ } >+ int sum(int[5][6][7] array) >+ { >+ int result = 0; >+ for (uint i = array.length; i--;) >+ result += sum(array[i]); >+ return result; >+ } >+ void setValues(thread int[][6][7] array) >+ { >+ for (uint i = array.length; i--;) { >+ for (uint j = array[i].length; j--;) { >+ for (uint k = array[i][j].length; k--;) >+ array[i][j][k] = int(i + j + k); >+ } >+ } >+ } >+ test int testSetValuesAndSum() >+ { >+ int[5][6][7] array; >+ setValues(@array); >+ return sum(array); >+ } >+ test int testSetValuesMutateValuesAndSum() >+ { >+ int[5][6][7] array; >+ setValues(@array); >+ for (uint i = array.length; i--;) { >+ for (uint j = array[i].length; j--;) { >+ for (uint k = array[i][j].length; k--;) >+ array[i][j][k] = array[i][j][k] * int(k); >+ } >+ } >+ return sum(array); >+ } >+ `); >+ checkInt(program, callFunction(program, "testSetValuesAndSum", []), 1575); >+ checkInt(program, callFunction(program, "testSetValuesMutateValuesAndSum", []), 5565); >+} >+ >+tests.lotsOfLocalVariables = function() >+{ >+ let src = "test int sum() {\n"; >+ src += " int i = 0;\n"; >+ let target = 0; >+ const numVars = 1024; >+ for (let i = 0; i < numVars; i++) { >+ const value = i * 3; >+ src += ` i = ${i};\n`; >+ src += ` int V${i} = (i + 3) * (i + 3);\n`; >+ target += (i + 3) * (i + 3); >+ } >+ src += " int result = 0;\n"; >+ for (let i = 0; i < numVars; i++) { >+ src += ` result += V${i};\n`; >+ } >+ src += " return result;\n"; >+ src += "}"; >+ let program = doPrep(src); >+ checkInt(program, callFunction(program, "sum", []), target); >+} >+ > tests.operatorBool = function() > { > let program = doPrep(` >@@ -7939,13 +8103,6 @@ function* doTest(testFilter) > throw new Error("Test setup is incomplete."); > let before = preciseTime(); > >- print("Compiling standard library..."); >- const compileBefore = preciseTime(); >- yield; >- prepare(); >- const compileAfter = preciseTime(); >- print(` OK, took ${Math.round((compileAfter - compileBefore) * 1000)} ms`); >- > let names = []; > for (let s in tests) > names.push(s); >diff --git a/Tools/WebGPUShadingLanguageRI/Type.js b/Tools/WebGPUShadingLanguageRI/Type.js >index 2db158d099b326a93e7335e4e696138c5edbefdb..9a5e56e501d8e81e0a85a5f5c2960eb462eccbb3 100644 >--- a/Tools/WebGPUShadingLanguageRI/Type.js >+++ b/Tools/WebGPUShadingLanguageRI/Type.js >@@ -36,6 +36,11 @@ class Type extends Node { > get isEnum() { return false; } > get isPrimitive() { return false; } > >+ get arrayRefType() >+ { >+ return new ArrayRefType(this.origin, "thread", this); >+ } >+ > // Have to call these on the unifyNode. > argumentForAndOverload(origin, value) > {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 187735
:
345174
|
345185
|
345208
|
345219
|
345220
|
348257
|
348407
|
348703
|
348852
|
348869
|
348884
|
349505
|
349524
|
349612
|
349696
|
349733
|
349735
|
349817
|
349826
|
349839
|
349939
|
349957
|
350095
| 350174