WebKit Bugzilla
Attachment 348257 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]
WIP
bug-187735-20180827200614.patch (text/plain), 173.03 KB, created by
Thomas Denney
on 2018-08-27 20:06:19 PDT
(
hide
)
Description:
WIP
Filename:
MIME Type:
Creator:
Thomas Denney
Created:
2018-08-27 20:06:19 PDT
Size:
173.03 KB
patch
obsolete
>Subversion Revision: 235371 >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 8f7639f6aa43120de446aebbf7e83a225ba3f7d5..7199e78c3bda2048591f82d405c38e4c804511bb 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,85 @@ >+2018-08-27 Thomas Denney <tdenney@apple.com> >+ >+ Adding WHLSL compiler >+ https://bugs.webkit.org/show_bug.cgi?id=187735 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * WebGPUShadingLanguageRI/FlattenedStructOffsetGatherer.js: >+ (FlattenedStructOffsetGatherer.prototype.visitTypeRef): >+ * WebGPUShadingLanguageRI/Func.js: >+ (Func.prototype.get isEntryPoint): >+ * WebGPUShadingLanguageRI/Intrinsics.js: >+ (Intrinsics): >+ * WebGPUShadingLanguageRI/MakeArrayRefExpression.js: >+ (MakeArrayRefExpression): >+ * WebGPUShadingLanguageRI/Metal/.gitignore: Added. >+ * WebGPUShadingLanguageRI/Metal/ArrayRefDefinition.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/CompileResult.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/ConstexprEmitter.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/Counter.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/Deinliner.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/FindTopLevelTypeAttributes.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/ForwardDeclarations.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/LineNumbers.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLLexer.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MSLTypeNamer.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/MetalCodegen.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/NameMangler.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/README.md: Added. >+ * WebGPUShadingLanguageRI/Metal/StructCodegen.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/StructFinalDefinition.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/StructForwardDeclaration.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/Token.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/TypeAttributes.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/TypeOfExpression.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/TypeSorter.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/TypeUnifier.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/VarOrFieldDeclaration.ts: Added. >+ * WebGPUShadingLanguageRI/Metal/index.html: Added. >+ * WebGPUShadingLanguageRI/Metal/metal.css: Added. >+ (textarea, .code-source): >+ (input[type=button]): >+ (.code-source): >+ (td): >+ (body): >+ (h1, h2): >+ (table): >+ (.container-cell): >+ (th, td): >+ (.code-comment): >+ (.code-comment-note): >+ (.code-keyword, .code-type): >+ (.code-literal): >+ (.code-attribute): >+ (.code-whitespace): >+ (.code-error): >+ (.code-directive): >+ (.line-no): >+ (.line-no::before): >+ (.line): >+ * WebGPUShadingLanguageRI/Metal/tsconfig.json: Added. >+ * WebGPUShadingLanguageRI/SynthesizeStructAccessors.js: >+ (synthesizeStructAccessors.setupImplementationData): >+ >+2018-08-27 Thomas Denney <tdenney@apple.com> >+ >+ Allow new vector types to work with the interactive interpreter >+ https://bugs.webkit.org/show_bug.cgi?id=188988 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * WebGPUShadingLanguageRI/FlattenedStructOffsetGatherer.js: >+ (FlattenedStructOffsetGatherer.prototype.visitTypeRef): Do not >+ unncessarily visit the type arguments of a TypeRef, as by this point >+ there are none that are relevant. >+ * WebGPUShadingLanguageRI/Intrinsics.js: >+ (Intrinsics): Treat VectorType as a primitive type. >+ > 2018-08-27 Alex Christensen <achristensen@webkit.org> > > Fix GTK build. >diff --git a/Tools/WebGPUShadingLanguageRI/FlattenedStructOffsetGatherer.js b/Tools/WebGPUShadingLanguageRI/FlattenedStructOffsetGatherer.js >index 1970f8ed932cd3530a48dc31af8c9c4240dde8d0..53c8716c45046f87fc4f6cd53fa9f7a42c53e165 100644 >--- a/Tools/WebGPUShadingLanguageRI/FlattenedStructOffsetGatherer.js >+++ b/Tools/WebGPUShadingLanguageRI/FlattenedStructOffsetGatherer.js >@@ -59,7 +59,6 @@ class FlattenedStructOffsetGatherer extends Visitor { > > visitTypeRef(node) > { >- super.visitTypeRef(node); > Node.visit(node.type, this); > } > >diff --git a/Tools/WebGPUShadingLanguageRI/Func.js b/Tools/WebGPUShadingLanguageRI/Func.js >index cbc2c333f666f687c07d33dc0188cc9846a3f6d2..5c328bb31dfe150d7934484cb1a3e49f27c5be74 100644 >--- a/Tools/WebGPUShadingLanguageRI/Func.js >+++ b/Tools/WebGPUShadingLanguageRI/Func.js >@@ -51,7 +51,7 @@ class Func extends Node { > get parameterTypes() { return this.parameters.map(parameter => parameter.type); } > get isCast() { return this._isCast; } > get shaderType() { return this._shaderType; } >- get isEntryPoint() { return this.shaderType != null; } >+ get isEntryPoint() { return !!this.shaderType; } > get returnTypeForOverloadResolution() { return this.isCast ? this.returnType : null; } > > get kind() { return Func; } >diff --git a/Tools/WebGPUShadingLanguageRI/Intrinsics.js b/Tools/WebGPUShadingLanguageRI/Intrinsics.js >index e9c3d1b34176a4bfd79b4f4494b37be0e5e9b380..72290632982ee108412e88b0882e41ce85a7301d 100644 >--- a/Tools/WebGPUShadingLanguageRI/Intrinsics.js >+++ b/Tools/WebGPUShadingLanguageRI/Intrinsics.js >@@ -311,6 +311,7 @@ class Intrinsics { > for (let vectorSize of VectorElementSizes) { > this._map.set(`native typedef vector<${vectorType}, ${vectorSize}>`, type => { > this[`vector<${vectorType}, ${vectorSize}>`] = type; >+ type.isPrimitive = true; > }); > } > } >diff --git a/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js b/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js >index 0cc6f6ecd23bf03f0ae223f192b1a07ad443709e..511dbc310d1edb288f467c597cf56b35c110387c 100644 >--- a/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js >+++ b/Tools/WebGPUShadingLanguageRI/MakeArrayRefExpression.js >@@ -29,8 +29,11 @@ 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); >+ if (this.lValue.variable && this.lValue.variable.type) { >+ if (this.lValue.variable.type.isArray && this.lValue.variable.type.elementType) >+ this._type = new ArrayRefType(origin, "thread", this.lValue.variable.type.elementType); >+ else >+ this._type = new ArrayRefType(origin, "thread", this.lValue.variable.type); > } > } > >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/.gitignore b/Tools/WebGPUShadingLanguageRI/Metal/.gitignore >new file mode 100644 >index 0000000000000000000000000000000000000000..35434ffb59d05621b1d662b7d989a497bf8dd9fa >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/.gitignore >@@ -0,0 +1,2 @@ >+compiler.js >+compiler.js.map >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/ArrayRefDefinition.ts b/Tools/WebGPUShadingLanguageRI/Metal/ArrayRefDefinition.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..e3a9f38c961116fbe8f852c564bf20277148811a >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/ArrayRefDefinition.ts >@@ -0,0 +1,50 @@ >+/* >+ * 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. >+ */ >+ >+// Writes the definitions of the structures used for the different array ref types present in the program. >+class ArrayRefDefinition { >+ private _arrayRefType: ArrayRefType; >+ private _typeNamer: MSLTypeNamer; >+ >+ constructor(node, typeNamer) >+ { >+ this._arrayRefType = node; >+ this._typeNamer = typeNamer; >+ } >+ >+ get arrayRefType() >+ { >+ return this._arrayRefType; >+ } >+ >+ toString() >+ { >+ let src = `struct ${this._typeNamer.uniqueTypeId(this.arrayRefType)} {\n`; >+ src += ` ${this.arrayRefType.addressSpace} ${this._typeNamer.mslTypeName(this.arrayRefType.elementType)}* ptr;\n`; >+ src += ` size_t length;\n`; >+ src += `};`; >+ return src; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/CompileResult.ts b/Tools/WebGPUShadingLanguageRI/Metal/CompileResult.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..aad082870c2dd0439f0626766c3b836a1b6cf2bf >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/CompileResult.ts >@@ -0,0 +1,65 @@ >+/* >+ * 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 CompileResult { >+ >+ private _metalShaderLanguageSource: string; >+ private _error: Error; >+ private _originalFunctionNameToMangledNames: any; >+ private _functionSources: Map<string, string>; >+ >+ constructor(src: string, err: Error, mangledNameMap: any, functionSources: Map<string, string>) >+ { >+ this._metalShaderLanguageSource = src; >+ this._error = err; >+ this._originalFunctionNameToMangledNames = mangledNameMap; >+ this._functionSources = functionSources; >+ } >+ >+ get metalShaderLanguageSource(): string >+ { >+ return this._metalShaderLanguageSource; >+ } >+ >+ get error(): Error >+ { >+ return this._error; >+ } >+ >+ get originalFunctionNameToMangledNames() >+ { >+ return this._originalFunctionNameToMangledNames; >+ } >+ >+ get functionSources() >+ { >+ return this._functionSources; >+ } >+ >+ get didSucceed() >+ { >+ return this._error == null; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/ConstexprEmitter.ts b/Tools/WebGPUShadingLanguageRI/Metal/ConstexprEmitter.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..8ae12f389a09e7ab87ca18039984a7904e5ee664 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/ConstexprEmitter.ts >@@ -0,0 +1,48 @@ >+/* >+ * 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 ConstexprEmitter extends Visitor >+{ >+ visitBoolLiteral(node) >+ { >+ return node.value.toString(); >+ } >+ >+ visitEnumLiteral(node) >+ { >+ return node.member.value.toString(); >+ } >+ >+ visitGenericLiteral(node) >+ { >+ return node.value.toString(); >+ } >+ >+ visitNullLiteral(node) >+ { >+ return "(nullptr)"; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/Counter.ts b/Tools/WebGPUShadingLanguageRI/Metal/Counter.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..9ef38cdf412082487991237b7d7ab1b916468e86 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/Counter.ts >@@ -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. >+ */ >+ >+// Provides unique names to variables, functions, and types. >+class Counter { >+ private _prefix: string; >+ private _value: number; >+ >+ constructor(prefix: string) >+ { >+ this._prefix = prefix; >+ this._value = 0; >+ } >+ >+ fresh(): string >+ { >+ return `${this._prefix}${this._value++}`; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/Deinliner.ts b/Tools/WebGPUShadingLanguageRI/Metal/Deinliner.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..c12271554b92b45fde3422f8e12d01b2fc17a24b >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/Deinliner.ts >@@ -0,0 +1,387 @@ >+/* >+ * Copyright (C) 2017 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. >+ */ >+ >+// Determines whether a function has reducible control flow or potential early returns. >+// This is a conservative estimate (e.g. in the case of loops). >+function functionLikeBlockCanBeInlined(node: FunctionLikeBlock) >+{ >+ enum ReturnStyle { >+ Never, >+ SometimesEarly, >+ Always >+ }; >+ >+ // A loop body might always return, but the loop body is only executed if the loop's condition holds. >+ // We therefore downgrade our certainty for the whole loop. >+ const beConservativeWithAlways = (a: ReturnStyle) => { >+ return a == ReturnStyle.Always ? ReturnStyle.SometimesEarly : a; >+ }; >+ >+ // Used wheere statement a immediately preceeds statement b. >+ const combineReturnStylesForSubsequents = (a: ReturnStyle, b: ReturnStyle) => { >+ if (a == ReturnStyle.Always || a == ReturnStyle.SometimesEarly) >+ return a; >+ return b; >+ }; >+ >+ // Used when statement a is the if branch, statement b is the else branch >+ const combineReturnStylesForAlternatives = (a: ReturnStyle, b: ReturnStyle) => { >+ switch (a) { >+ case ReturnStyle.Never: >+ switch (b) { >+ case ReturnStyle.Never: >+ return ReturnStyle.Never; >+ case ReturnStyle.Always: >+ return ReturnStyle.SometimesEarly; >+ case ReturnStyle.SometimesEarly: >+ return ReturnStyle.SometimesEarly; >+ } >+ case ReturnStyle.SometimesEarly: >+ return ReturnStyle.SometimesEarly; >+ case ReturnStyle.Always: >+ switch (b) { >+ case ReturnStyle.Never: >+ case ReturnStyle.SometimesEarly: >+ return ReturnStyle.SometimesEarly; >+ case ReturnStyle.Always: >+ return ReturnStyle.Always; >+ } >+ } >+ } >+ >+ class ReturnStyleVisitor extends Visitor { >+ private _visitList(list: Node[]) >+ { >+ let style = ReturnStyle.Never; >+ for (let statement of list) { >+ style = combineReturnStylesForSubsequents(style, returnStyleForNodeOrNull(statement)); >+ if (style != ReturnStyle.Never) >+ return style; >+ } >+ return style; >+ } >+ >+ visitAnonymousVariable(node: AnonymousVariable) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitAssignment(node: Assignment) >+ { >+ return this._visitList([ node.lhs, node.rhs ]); >+ } >+ >+ visitBlock(node: Block) >+ { >+ return this._visitList(node.statements); >+ } >+ >+ visitBreak(node: Break) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitCallExpression(node: CallExpression) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitCommaExpression(node: CommaExpression) >+ { >+ return this._visitList(node.list); >+ } >+ >+ visitContinue(node: Continue) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitDereferenceExpression(node: DereferenceExpression) >+ { >+ return returnStyleForNodeOrNull(node.ptr); >+ } >+ >+ visitDotExpression(node: DotExpression) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitDoWhileLoop(node: DoWhileLoop) >+ { >+ return beConservativeWithAlways(this._visitList([ node.body, node.conditional ])); >+ } >+ >+ visitForLoop(node: ForLoop) >+ { >+ return beConservativeWithAlways(this._visitList([ node.initialization, node.condition, node.body, node.increment ])); >+ } >+ >+ visitFuncDef(node: FuncDef) >+ { >+ return returnStyleForNodeOrNull(node.body); >+ } >+ >+ visitFunctionLikeBlock(node: FunctionLikeBlock) >+ { >+ return returnStyleForNodeOrNull(node.func) == ReturnStyle.SometimesEarly ? ReturnStyle.SometimesEarly : ReturnStyle.Never; >+ } >+ >+ visitGenericLiteral(node: GenericLiteral) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitIdentityExpression(node: IdentityExpression) >+ { >+ return returnStyleForNodeOrNull(node.target); >+ } >+ >+ visitIfStatement(node: IfStatement) >+ { >+ const conditional = returnStyleForNodeOrNull(node.conditional); >+ const body = returnStyleForNodeOrNull(node.body); >+ const elseBody = returnStyleForNodeOrNull(node.elseBody); >+ return combineReturnStylesForSubsequents(conditional, combineReturnStylesForAlternatives(body, elseBody)); >+ } >+ >+ visitIndexExpression(node: IndexExpression) >+ { >+ return this._visitList([ node.array, node.index ]); >+ } >+ >+ visitLogicalExpression(node: LogicalExpression) >+ { >+ return this._visitList([ node.left, node.right ]); >+ } >+ >+ visitLogicalNot(node: LogicalNot) >+ { >+ return returnStyleForNodeOrNull(node.operand); >+ } >+ >+ visitMakeArrayRefExpression(node: MakeArrayRefExpression) >+ { >+ return returnStyleForNodeOrNull(node.lValue); >+ } >+ >+ visitMakePtrExpression(node: MakePtrExpression) >+ { >+ return returnStyleForNodeOrNull(node.lValue); >+ } >+ >+ visitNullLiteral(node: NullLiteral) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitReturn(node: Return) >+ { >+ return ReturnStyle.Always; >+ } >+ >+ visitSwitchCase(node: SwitchCase) >+ { >+ return returnStyleForNodeOrNull(node.body); >+ } >+ >+ visitSwitchStatement(node: SwitchStatement) >+ { >+ >+ // return within switch statements is awkward, so we prevent their inlining. >+ // Its fine if the switch is a top level statement of the function and every branch returns, >+ // including a default case, but if that switch is inside a loop or similar we start hitting issues. >+ return beConservativeWithAlways(this._visitList(node.switchCases)); >+ } >+ >+ visitTrapStatement(node: TrapStatement) >+ { >+ return ReturnStyle.Always; >+ } >+ >+ visitVariableDecl(node: VariableDecl) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitVariableRef(node: VariableRef) >+ { >+ return ReturnStyle.Never; >+ } >+ >+ visitWhileLoop(node: WhileLoop) >+ { >+ return beConservativeWithAlways(this._visitList([ node.conditional, node.body ])); >+ } >+ >+ visitArrayRefType(node: ArrayRefType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitArrayRefType"); >+ } >+ >+ visitArrayType(node: ArrayType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitArrayType"); >+ } >+ >+ visitBoolLiteral(node: BoolLiteral) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitBoolLiteral"); >+ } >+ >+ visitConstexprTypeParameter(node: ConstexprTypeParameter) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitConstexprTypeParameter"); >+ } >+ >+ visitConvertPtrToArrayRefExpression(node: ConvertPtrToArrayRefExpression) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitConvertPtrToArrayRefExpression"); >+ } >+ >+ visitElementalType(node: ElementalType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitElementalType"); >+ } >+ >+ visitEnumLiteral(node: EnumLiteral) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitEnumLiteral"); >+ } >+ >+ visitEnumMember(node: EnumMember) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitEnumMember"); >+ } >+ >+ visitEnumType(node: EnumType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitEnumType"); >+ } >+ >+ visitField(node: Field) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitField"); >+ } >+ >+ visitFunc(node: Func) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitFunc"); >+ } >+ >+ visitFuncParameter(node: FuncParameter) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitFuncParameter"); >+ } >+ >+ visitGenericLiteralType(node: GenericLiteralType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitGenericLiteralType"); >+ } >+ >+ visitNativeFunc(node: NativeFunc) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitNativeFunc"); >+ } >+ >+ visitNativeType(node: NativeType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitNativeType"); >+ } >+ >+ visitNativeTypeInstance(node: NativeTypeInstance) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitNativeTypeInstance"); >+ } >+ >+ visitNullType(node: NullType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitNullType"); >+ } >+ >+ visitProgram(node: Program) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitProgram"); >+ } >+ >+ visitProtocolDecl(node: ProtocolDecl) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitProtocolDecl"); >+ } >+ >+ visitProtocolFuncDecl(node: ProtocolFuncDecl) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitProtocolFuncDecl"); >+ } >+ >+ visitProtocolRef(node: ProtocolRef) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitProtocolRef"); >+ } >+ >+ visitPtrType(node: PtrType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitPtrType"); >+ } >+ >+ visitReadModifyWriteExpression(node: ReadModifyWriteExpression) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitReadModifyWriteExpression"); >+ } >+ >+ visitReferenceType(node: ReferenceType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitReferenceType"); >+ } >+ >+ visitStructType(node: StructType) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitStructType"); >+ } >+ >+ visitTypeDef(node: TypeDef) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitTypeDef"); >+ } >+ >+ visitTypeRef(node: TypeRef) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitTypeRef"); >+ } >+ >+ visitTypeVariable(node: TypeVariable) >+ { >+ throw new Error("Unimplemented ReturnStyleVisitor.visitTypeVariable"); >+ } >+ } >+ >+ const returnStyleVisitor = new ReturnStyleVisitor(); >+ >+ function returnStyleForNodeOrNull(node: Node) >+ { >+ return node ? Node.visit(node, returnStyleVisitor) : ReturnStyle.Never; >+ } >+ >+ return returnStyleForNodeOrNull(node) != ReturnStyle.SometimesEarly; >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/FindTopLevelTypeAttributes.ts b/Tools/WebGPUShadingLanguageRI/Metal/FindTopLevelTypeAttributes.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..cc72640576339accbb6daf5a1aebe82f98933f70 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/FindTopLevelTypeAttributes.ts >@@ -0,0 +1,91 @@ >+/* >+ * 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 FindTopLevelTypeAttributes extends Visitor { >+ private _typeUnifier : TypeUnifier; >+ private _typeAttributeMap : Map<string, TypeAttributes>; >+ >+ constructor(functionDefs, typeUnifier) >+ { >+ super(); >+ this._typeUnifier = typeUnifier; >+ >+ this._typeAttributeMap = new Map(); >+ >+ for (let func of functionDefs) >+ Node.visit(func.func, this); >+ } >+ >+ _keyForType(type) >+ { >+ return this._typeUnifier.uniqueTypeId(type); >+ } >+ >+ hasAttributesForType(type) >+ { >+ return this._typeAttributeMap.has(this._keyForType(type)); >+ } >+ >+ attributesForType(type) >+ { >+ const key = this._keyForType(type); >+ let attrs = this._typeAttributeMap.get(key); >+ if (!attrs) { >+ attrs = new TypeAttributes(type); >+ this._typeAttributeMap.set(key, attrs); >+ } >+ return attrs; >+ } >+ >+ visitFuncDef(func) >+ { >+ switch (func.shaderType) { >+ case "vertex": >+ this.visitVertexShader(func); >+ break; >+ case "fragment": >+ this.visitFragmentShader(func); >+ break; >+ } >+ } >+ >+ private visitVertexShader(func) >+ { >+ this.attributesForType(func.returnType).isVertexOutputOrFragmentInput = true; >+ for (let param of func.parameters) >+ this.attributesForType(param.type).isVertexAttribute = true; >+ } >+ >+ private visitFragmentShader(func) >+ { >+ for (let param of func.parameters) >+ this.attributesForType(param.type).isVertexOutputOrFragmentInput = true; >+ >+ this.attributesForType(func.returnType).isFragmentOutput = true; >+ } >+ >+ // TODO: Handle compute shaders >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/ForwardDeclarations.ts b/Tools/WebGPUShadingLanguageRI/Metal/ForwardDeclarations.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..76ccac2a697bc7cbdb3d12efa19c7777eaf627e9 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/ForwardDeclarations.ts >@@ -0,0 +1,357 @@ >+/* >+ * 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. >+ */ >+ >+// To avoid having to convert all of the existing classes in the interpreter to >+// TypeScript we instead just include all of the methods that we need here to >+// avoid type errors with TypeScript. >+ >+declare class Console { >+ log(message: string, ...args: any[]); >+} >+ >+declare const console: Console; >+ >+declare class Visitor { >+ visitAnonymousVariable(node: AnonymousVariable); >+ visitArrayRefType(node: ArrayRefType); >+ visitArrayType(node: ArrayType); >+ visitAssignment(node: Assignment); >+ visitBlock(node: Block); >+ visitBoolLiteral(node: BoolLiteral); >+ visitBreak(node: Break); >+ visitCallExpression(node: CallExpression); >+ visitCommaExpression(node: CommaExpression); >+ visitConstexprTypeParameter(node: ConstexprTypeParameter); >+ visitContinue(node: Continue); >+ visitConvertPtrToArrayRefExpression(node: ConvertPtrToArrayRefExpression); >+ visitDereferenceExpression(node: DereferenceExpression); >+ visitDotExpression(node: DotExpression); >+ visitDoWhileLoop(node: DoWhileLoop); >+ visitElementalType(node: ElementalType); >+ visitEnumLiteral(node: EnumLiteral); >+ visitEnumMember(node: EnumMember); >+ visitEnumType(node: EnumType); >+ visitField(node: Field); >+ visitForLoop(node: ForLoop); >+ visitFunc(node: Func); >+ visitFuncDef(node: FuncDef); >+ visitFuncParameter(node: FuncParameter); >+ visitFunctionLikeBlock(node: FunctionLikeBlock); >+ visitGenericLiteral(node: GenericLiteral); >+ visitGenericLiteralType(node: GenericLiteralType); >+ visitIdentityExpression(node: IdentityExpression); >+ visitIfStatement(node: IfStatement); >+ visitIndexExpression(node: IndexExpression); >+ visitLogicalExpression(node: LogicalExpression); >+ visitLogicalNot(node: LogicalNot); >+ visitMakeArrayRefExpression(node: MakeArrayRefExpression); >+ visitMakePtrExpression(node: MakePtrExpression); >+ visitNativeFunc(node: NativeFunc); >+ visitNativeType(node: NativeType); >+ visitNativeTypeInstance(node: NativeTypeInstance); >+ visitNullLiteral(node: NullLiteral); >+ visitNullType(node: NullType); >+ visitProgram(node: Program); >+ visitProtocolDecl(node: ProtocolDecl); >+ visitProtocolFuncDecl(node: ProtocolFuncDecl); >+ visitProtocolRef(node: ProtocolRef); >+ visitPtrType(node: PtrType); >+ visitReadModifyWriteExpression(node: ReadModifyWriteExpression); >+ visitReferenceType(node: ReferenceType); >+ visitReturn(node: Return); >+ visitStructType(node: StructType); >+ visitSwitchCase(node: SwitchCase); >+ visitSwitchStatement(node: SwitchStatement); >+ visitTrapStatement(node: TrapStatement); >+ visitTypeDef(node: TypeDef); >+ visitTypeRef(node: TypeRef); >+ visitTypeVariable(node: TypeVariable); >+ visitVariableDecl(node: VariableDecl); >+ visitVariableRef(node: VariableRef); >+ visitWhileLoop(node: WhileLoop); >+} >+ >+declare class Rewriter {} >+ >+declare class Assignment extends Node { >+ lhs: Node; >+ rhs: Node; >+} >+ >+declare class AnonymousVariable extends Node {} >+ >+declare class ArrayRefType extends Type { >+ constructor(origin, addressSpace, elementType); >+ >+ addressSpace: string; >+ elementType: Type; >+} >+ >+declare class ArrayType extends Type { >+ constructor(origin, elementType, numElements); >+ >+ elementType: Type; >+ numElements: any; >+} >+ >+declare class Block extends Node { >+ constructor(origin: any); >+ >+ add(node: Node); >+ >+ statements: Node[]; >+} >+ >+declare class BoolLiteral extends Node { >+ constructor(origin, value); >+} >+ >+declare class CallExpression extends Node { >+ constructor(origin, name, argumentList); >+ >+ argumentTypes: any; >+ func: any; >+ possibleOverloads: any; >+ resultType: any; >+ resultEPtr: any; >+ >+ setCastData(any); >+} >+ >+declare class CommaExpression extends Node { >+ list: Node[]; >+} >+ >+declare class DereferenceExpression extends Node { >+ ptr: Node; >+} >+ >+declare class DoWhileLoop extends Node { >+ constructor(origin, body, conditional); >+ >+ body: Node; >+ conditional: Node; >+} >+ >+declare class EnumType extends Type { >+ constructor(origin, name, baseType); >+} >+ >+declare class Field extends Node { >+ constructor(origin, name, type); >+ >+ name: string; >+ type: Type; >+} >+ >+declare class ForLoop extends Node { >+ initialization: Node; >+ condition: Node; >+ increment: Node; >+ body: Node; >+} >+ >+declare class Func extends Node { >+ constructor(origin, name, returnType, typeParameters, parameters, isCast, shaderType); >+ >+ name: string; >+ origin: Origin; >+ parameters: FuncParameter[]; >+ returnType: Type; >+ shaderType: string; >+ isEntryPoint: boolean; >+} >+ >+declare class FunctionLikeBlock extends Node { >+ func: Func; >+} >+ >+declare class FuncDef extends Func { >+ constructor(origin, name, returnType, typeParameters, parameters, body, isCast, shaderType); >+ >+ body: Node; >+} >+ >+declare class FuncInstantiator { >+ instances: any[]; >+} >+ >+declare class FuncParameter extends Value { >+ constructor(origin: Origin, name: string, type: Type); >+ >+ origin: Origin; >+ name: string; >+ type: Type; >+ varIsLValue: boolean; >+} >+ >+declare class IdentityExpression extends Node { >+ target: Node; >+} >+ >+declare class IfStatement { >+ constructor(origin, conditional, body, elseBody); >+ >+ conditional: Node; >+ body: Node; >+ elseBody: Node; >+} >+ >+declare class IndexExpression extends Node { >+ array: Node; >+ index: Node; >+} >+ >+declare class LogicalExpression extends Node { >+ text: string; >+ left: Node; >+ right: Node; >+} >+ >+declare class LogicalNot extends Node { >+ operand: Node; >+} >+ >+declare class MakeArrayRefExpression extends Node { >+ lValue: any; >+ numElements: any; >+} >+ >+declare class MakePtrExpression extends Node { >+ lValue: any; >+} >+ >+declare class Node { >+ static visit(func: any, visitor: Visitor | Rewriter); >+ visit(visitor: Visitor); >+ >+ origin: Origin; >+} >+ >+declare class Origin { >+ index: number; >+} >+ >+declare class Program { >+ topLevelStatements: Node[]; >+ functions: Map<string, Func[]>; >+ types: Type[]; >+ funcInstantiator: FuncInstantiator; >+} >+ >+declare class PtrType extends Type { >+ constructor(origin, addressSpace, elementType); >+} >+ >+declare class StructType extends Type { >+ constructor(origin, name, typeParameters); >+ >+ fieldMap: Map<string, Field>; >+ fields: Field[]; >+ >+ add(field: Field); >+} >+ >+declare class SwitchCase extends Node { >+ isDefault: boolean; >+ value: Node; >+ body: Node; >+} >+ >+declare class SwitchStatement extends Node { >+ switchCases: SwitchCase[]; >+} >+ >+declare class SwizzleOp { >+ constructor(outSize: number, components: string[], inSize: number); >+ >+ outSize: number; >+ components: string[]; >+ inSize: number; >+} >+ >+declare class Type extends Node { >+ name: string; >+ isArray: boolean; >+} >+ >+declare class VariableRef extends Node {} >+ >+declare class Value extends Node {} >+ >+declare class WhileLoop extends Node { >+ constructor(origin, condition, body); >+ >+ conditional: Node; >+ body: Node; >+} >+ >+declare function prepare(file: string, lineOffset: number, src: string): Program; >+ >+// TODO >+declare class Break extends Node {} >+declare class ConstexprTypeParameter extends Node {} >+declare class Continue extends Node {} >+declare class ConvertPtrToArrayRefExpression extends Node {} >+declare class DotExpression extends Node {} >+declare class ElementalType extends Node {} >+declare class EnumLiteral extends Node {} >+declare class EnumMember extends Node {} >+declare class GenericLiteral extends Node {} >+declare class GenericLiteralType extends Node {} >+declare class NativeFunc extends Func {} >+declare class NativeType extends Node {} >+declare class NativeTypeInstance extends Node {} >+declare class NullLiteral extends Node {} >+declare class NullType extends Node {} >+declare class ProtocolDecl extends Node {} >+declare class ProtocolFuncDecl extends Node {} >+declare class ProtocolRef extends Node {} >+declare class ReadModifyWriteExpression extends Node {} >+declare class ReferenceType extends Node {} >+declare class Return extends Node {} >+declare class TrapStatement extends Node {} >+declare class TypeDef extends Node {} >+declare class TypeRef extends Node {} >+declare class TypeVariable extends Node {} >+declare class VariableDecl extends Node {} >+ >+declare class BuiltinVectorGetter { >+ baseTypeName: any; >+ size: any; >+ elementName: any; >+ index: any; >+} >+ >+declare class BuiltinVectorSetter { >+ baseTypeName: any; >+ size: any; >+ elementName: any; >+ index: any; >+} >+ >+declare class BuiltinVectorIndexGetter {}; >+declare class BuiltinVectorIndexSetter {}; >+declare class BuiltinVectorEqualityOperator {}; >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/LineNumbers.ts b/Tools/WebGPUShadingLanguageRI/Metal/LineNumbers.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..9c7c988439ef401c226839f1053c03e1f6779191 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/LineNumbers.ts >@@ -0,0 +1,38 @@ >+/* >+ * 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. >+ */ >+ >+// This must be called on the original WHLSL source before MSL compilation >+// to allow translation from character indices to line numbers. >+function lineNumbers(src: string): number[] >+{ >+ const lineNumbers = []; >+ let lineNumber = 1; >+ for (let i = 0; i < src.length; i++) { >+ lineNumbers.push(lineNumber); >+ if (src[i] == '\n') >+ lineNumber++; >+ } >+ return lineNumbers; >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.ts b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..3aa1643e7529a5cc55009f83519a8c8da12f0e3b >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDeclaration.ts >@@ -0,0 +1,143 @@ >+/* >+ * 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 for the first line of a function declaration or definition. >+class MSLFunctionDeclaration { >+ private _funcMangler: NameMangler; >+ private _func: FuncDef; >+ private _typeNamer: MSLTypeNamer; >+ private _mangledName: string; >+ private _lineNumbers: number[]; >+ private _typeAttributes: FindTopLevelTypeAttributes; >+ >+ constructor(funcMangler, funcDef, typeNamer, mangledName, lineNos, typeAttributes) >+ { >+ this._funcMangler = funcMangler; >+ this._func = funcDef; >+ this._typeNamer = typeNamer; >+ this._mangledName = mangledName; >+ this._lineNumbers = lineNos; >+ this._typeAttributes = typeAttributes; >+ } >+ >+ get funcMangler(): NameMangler >+ { >+ return this._funcMangler; >+ } >+ >+ get func(): FuncDef >+ { >+ return this._func; >+ } >+ >+ get mangledName(): string >+ { >+ return this._mangledName; >+ } >+ >+ get typeNamer(): MSLTypeNamer >+ { >+ return this._typeNamer; >+ } >+ >+ get lineNumbers(): number[] >+ { >+ return this._lineNumbers; >+ } >+ >+ get typeAttributes(): FindTopLevelTypeAttributes >+ { >+ 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.name, `P${counter++}`); >+ return map; >+ } >+ >+ commentLine() >+ { >+ const params = []; >+ for (let paramName of this.paramMap.keys()) >+ params.push(paramName); >+ let line = `// ${this._func.name}(${params.join(", ")})`; >+ if (this._lineNumbers && this._func.origin.index && this._lineNumbers[this._func.origin.index]) >+ line += ` @ line ${this._lineNumbers[this._func.origin.index]}`; >+ return line + "\n"; >+ } >+ >+ toString() >+ { >+ let declLine = this.commentLine(); >+ >+ // TODO: Support shader types other than fragment and vertex. >+ // What WSL calls `compute` shaders Metal calls `kernel` shaders. >+ // We're only focussing on `vertex` and `fragment` for now. >+ if (this._func.shaderType === "vertex" || this._func.shaderType === "fragment") >+ declLine += `${this._func.shaderType} `; >+ declLine += `${this._typeNamer.mslTypeName(this._func.returnType)} `; >+ declLine += this._mangledName; >+ >+ declLine += "(" >+ >+ let params = []; >+ const paramMap = this.paramMap; >+ for (let param of this._func.parameters) { >+ let pStr = `${this._typeNamer.mslTypeName(param.type)} ${paramMap.get(param.name)}`; >+ // 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; >+ } >+ >+ parameterIsAttribute(node) >+ { >+ // We currently assuming that all parameters to entry points are attributes. >+ // TODO: Better logic for this, i.e. support samplers. >+ return this._func.isEntryPoint; >+ } >+ >+ 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.ts b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..d77b5d8839e50ce60e980a5979c45ff8a392d0f0 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionDefinition.ts >@@ -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 = super.toString(); >+ src += "\n{\n"; >+ let emitter = new MSLStatementEmitter(this.funcMangler, this.typeNamer, this.func, this.paramMap, this.func.name, this.lineNumbers, this.typeAttributes); >+ src += emitter.indentedSource(); >+ src += "}"; >+ return src; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.ts b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..57c6f81353c1f381f967345f8987f3cfb91da518 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLFunctionForwardDeclaration.ts >@@ -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 super.toString() + ";" >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLLexer.ts b/Tools/WebGPUShadingLanguageRI/Metal/MSLLexer.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..b2ed2645aa77651a05f11a15aca112c366b2b02d >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLLexer.ts >@@ -0,0 +1,121 @@ >+/* >+ * 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. >+ */ >+ >+// Does sufficient lexing for the output of the compiler, not for MSL in general! >+class MSLLexer { >+ private _tokens: Token[]; >+ >+ constructor(src) >+ { >+ this._tokens = this._lex(src); >+ } >+ >+ get tokens(): Token[] >+ { >+ return this._tokens; >+ } >+ >+ private get commentMatchers() >+ { >+ return [ >+ { type: "comment", regex: /^(\/\/|\/\*)(\s+)?/ }, >+ { type: "comment-note", regex: /^(TODO:|FIXME:)/ }, >+ { type: "comment", regex: /.*(\n|$)/ } >+ ] >+ } >+ >+ private get defaultMatchers() >+ { >+ return [ >+ { type: "comment", regex: /^\/\/(.*?)(\n|$)/, inner: this.commentMatchers }, >+ { type: "directive", regex: /^#(.*?)(\n|$)/ }, >+ { type: "whitespace", regex: /^\s+/ }, >+ { type: "literal", regex: /^([0-9]+)(\.([0-9]*?))?/ }, >+ { type: "type", regex: /^(struct|vertex|fragment|constant|const|threadgroup|thread|device|size_t|uint8_t|uint16_t|uint32_t|int8_t|int16_t|int32_t|((bool|uchar|char|ushort|short|uint|int|half|float)(2|3|4))|float|half|bool)/ }, >+ { type: "keyword", regex: /^(return|if|else|do|while|switch|case|default|break|continue|nullptr|true|false|using|namespace)/ }, >+ { type: "attribute", regex: /^\[\[([A-z][A-z_\(\)0-9]+)\]\]/, inner: [ >+ { type: "attribute", regex: /^\[\[[A-z]+\(/ }, >+ { type: "literal", regex: /^[0-9]+/ }, >+ { type: "attribute", regex: /^\)\]\]/ }, >+ { type: "attribute", regex: /^\[\[([A-z][A-z_\(\)0-9]+)\]\]/ } >+ ] }, >+ { type: "error", regex: /^undefined/ }, >+ { type: "identifier", regex: /^[A-z]([A-z0-9_]*)?/ }, >+ { type: "punctuation", regex: /^(;|\.|\(|\)|,|\+|\-|=|->|>|<|\!|\}|\{|\*|\?|\:|\[|\])/}, >+ { type: "unknown", regex: /^(.*?)(\n|$)/ } >+ ]; >+ } >+ >+ private _lex(src, initMatchers = null): Token[] >+ { >+ const tokens: Token[] = []; >+ const matchers = initMatchers ? initMatchers : this.defaultMatchers; >+ >+ const startOfMultilineComment = /^\/\*/; >+ const endOfMultilineComment = /\*\//; >+ >+ while (src.length) { >+ // Special case for multiline comments. >+ const startMatch = startOfMultilineComment.exec(src); >+ if (startMatch) { >+ const endMatch = endOfMultilineComment.exec(src); >+ if (endMatch) { >+ // Important to ignore the first two characters to avoid recursion. >+ tokens.push(new Token("comment", startMatch[0])); >+ const commentText = src.substring(2, endMatch.index + 2); >+ for (let innerTok of this._lex(commentText, this.commentMatchers)) >+ tokens.push(innerTok); >+ src = src.substring(endMatch.index + 2); >+ } else >+ throw new Error("Unmatched multiline comment"); >+ } >+ >+ let didMatch = false; >+ for (let matcher of matchers) { >+ const match = matcher.regex.exec(src); >+ if (match) { >+ if (matcher.inner) { >+ let innerToks = this._lex(match[0], matcher.inner); >+ for (let innerTok of innerToks) >+ tokens.push(innerTok); >+ } else >+ tokens.push(new Token(matcher.type, match[0])); >+ src = src.substring(match[0].length); >+ didMatch = true; >+ break; >+ } >+ } >+ if (!didMatch) >+ throw new Error(`Nothing matched ${src}`); >+ } >+ >+ return tokens; >+ } >+} >+ >+function lexMsl(msl: string): Token[] >+{ >+ return new MSLLexer(msl).tokens; >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.ts b/Tools/WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..129f1b56d06d27286324676df3d979e0b5b82c43 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLStatementEmitter.ts >@@ -0,0 +1,980 @@ >+/* >+ * 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 { >+ private _funcMangler: NameMangler; >+ private _typeNamer: MSLTypeNamer; >+ private _func: Func; >+ private _nameMap: Map<string, string>; >+ private _nameMapStack: Array<Map<string, string>>; >+ private _debugName: string; >+ private _lineNumbers: number[]; >+ private _typeAttributes: FindTopLevelTypeAttributes; >+ private _resultVar: string; >+ private _counter : Counter; >+ private _indentLevel: number; >+ private _lines: string[]; >+ private _ancestorReturnType : Type; >+ private _actualResult: string; >+ private _loopConditionVariableStack: string[] = []; >+ private _loopConditionNodeStack: Node[] = []; >+ private _loopIncrementNodeStack: Node[] = []; >+ >+ constructor(funcMangler: NameMangler, typeNamer: MSLTypeNamer, func: Func, paramMap: Map<string, string>, debugName: string, lineNos: number[], typeAttributes: FindTopLevelTypeAttributes, resultVar: string = null, counter: Counter = null, ancestorReturnType: Type = null) >+ { >+ super(); >+ this._funcMangler = funcMangler; >+ this._typeNamer = typeNamer; >+ this._func = func; >+ this._nameMap = paramMap; >+ this._nameMapStack = []; >+ this._debugName = debugName; >+ this._lineNumbers = lineNos; >+ this._typeAttributes = typeAttributes; >+ this._resultVar = resultVar; >+ this._counter = counter ? counter : new Counter("V"); >+ this._indentLevel = 0; >+ this._lines = []; >+ this._ancestorReturnType = ancestorReturnType ? ancestorReturnType : func.returnType; >+ >+ this._actualResult = Node.visit(this._func, this); >+ } >+ >+ get ancestorReturnType() >+ { >+ return this._ancestorReturnType; >+ } >+ >+ private _emitReturnZeroValue() >+ { >+ if (this.ancestorReturnType.name === "void") >+ this._add(`return;`); >+ else { >+ const resultVar = this._fresh(); >+ this._add(`${this._typeNamer.mslTypeName(this.ancestorReturnType)} ${resultVar};`); >+ this._zeroInitialize(this.ancestorReturnType, resultVar); >+ this._add(`return ${resultVar};`); >+ } >+ } >+ >+ // MSL uses the same default initialization rules as C++14, which means that the default value >+ // of a local variable is not deterministic. Therefore we zero initialise everything, and hope >+ // that LLVM is smart enough to avoid emitting anything if it doesn't need to. >+ private _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) >+ { >+ // FIXME: Why would this ever be null? >+ const typeAttributes = emitter._typeAttributes.attributesForType(node); >+ >+ for (let field of node.fields) { >+ let fieldName = field.name; >+ if (typeAttributes) >+ fieldName = typeAttributes.mangledFieldName(fieldName); >+ 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;`); >+ } >+ } >+ 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(""); >+ } >+ >+ private get _loopConditionVariable(): string >+ { >+ return this._loopConditionVariableStack[this._loopConditionVariableStack.length - 1]; >+ } >+ >+ private get _loopCondition(): Node >+ { >+ return this._loopConditionNodeStack[this._loopConditionNodeStack.length - 1]; >+ } >+ >+ private get _loopIncrement(): Node >+ { >+ return this._loopIncrementNodeStack[this._loopIncrementNodeStack.length - 1]; >+ } >+ >+ private _add(linesString) >+ { >+ for (let line of linesString.split('\n')) { >+ for (let i = 0; i < this._indentLevel; i++) >+ line = " " + line; >+ this._lines.push(line); >+ } >+ } >+ >+ private _addLines(lines) >+ { >+ for (let line of lines) >+ this._add(line); >+ } >+ >+ private _fresh() >+ { >+ return this._counter.fresh(); >+ } >+ >+ private _indent() >+ { >+ this._indentLevel++; >+ } >+ >+ private _unindent() >+ { >+ this._indentLevel--; >+ } >+ >+ // 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) >+ { >+ this._nameMapStack.push(this._nameMap); >+ this._nameMap = new Map(this._nameMap); >+ for (let stmt of block.statements) { >+ if (stmt.origin && stmt.origin.index && this._lineNumbers && this._lineNumbers[stmt.origin.index]) >+ this._add(`// ${this._debugName} @ line ${this._lineNumbers[stmt.origin.index]}`); >+ Node.visit(stmt, this); >+ } >+ this._nameMap = this._nameMapStack.pop(); >+ } >+ >+ visitReturn(node) >+ { >+ let expr = node.value.visit(this); >+ if (this._resultVar) >+ this._add(`${this._resultVar} = ${expr};`); >+ else >+ this._add(`return ${expr};`); >+ } >+ >+ private _inScope(emitBraces = false, emitBlock: () => any) >+ { >+ if (emitBraces) >+ this._add("{"); >+ this._indent(); >+ >+ this._nameMapStack.push(this._nameMap); >+ this._nameMap = new Map(this._nameMap); >+ >+ const res = emitBlock(); >+ >+ this._nameMap = this._nameMapStack.pop(); >+ if (emitBraces) >+ this._add("}"); >+ >+ this._unindent(); >+ >+ return res; >+ } >+ >+ private _emitScoped(node: Node, emitBraces = false) >+ { >+ if (!node) >+ throw new Error(`Can't emit scoped null node`); >+ >+ return this._inScope(emitBraces, () => Node.visit(node, this)); >+ } >+ >+ visitIfStatement(node) >+ { >+ let condition = node.conditional.visit(this); >+ this._add(`if (${condition}) {`); >+ this._emitScoped(node.body); >+ // At the moment the parser emits Block nodes for the if and else bodies, so this allows us >+ // to check whether or not the else body is actually empty, and therefore doesn't need to be emitted. >+ // (This isn't necessary, it just yields shorter, easier to read output). >+ const shouldEmitElseBody = node.elseBody && (!(node.elseBody instanceof Block) || node.elseBody.statements.length); >+ if (shouldEmitElseBody) { >+ this._add(`} else {`); >+ this._emitScoped(node.elseBody); >+ this._add('}'); >+ } else >+ this._add('}'); >+ } >+ >+ visitIdentityExpression(node) >+ { >+ return node.target.visit(this); >+ } >+ >+ visitLogicalNot(node) >+ { >+ const type = typeOf(node); >+ if (type) { >+ let expr = Node.visit(node.operand, this); >+ let id = this._fresh(); >+ this._add(`${this._typeNamer.mslTypeName(type)} ${id} = !(${expr});`); >+ return id; >+ } else >+ throw new Error(`Couldn't determine type of ! expression ${node}'`); >+ } >+ >+ visitDereferenceExpression(node) >+ { >+ /* >+ An assignment such as someStruct.field = someValue is parsed as >+ *((&someStruct)->value) = someValue >+ which isn't ideal, because we can't take the address of a member like .x/.y/.z in a SIMD type. >+ Therefore to avoid this pattern we determine if the expression we are dereferencing is >+ just producing a reference, because then we can avoid both steps. >+ */ >+ if (this._canAvoidDereferenceBecauseDereferencingReferenceNode(node.ptr)) >+ return this._doNotProduceReferenceFromReferenceNode(node.ptr); >+ else { >+ let result = node.ptr.visit(this); >+ return `*(${result})`; >+ } >+ } >+ >+ private _canAvoidDereferenceBecauseDereferencingReferenceNode(node) >+ { >+ const emitter = this; >+ >+ class PossibleRefVisitor extends Visitor { >+ visitMakePtrExpression(node) { return true; } >+ >+ visitCallExpression(node) >+ { >+ return emitter._isOperatorAnder(node) || emitter._isOperatorIndexer(node); >+ } >+ } >+ >+ // To avoid returning undefined/issues with functions not being defined. >+ return Node.visit(node, new PossibleRefVisitor()) ? true : false; >+ } >+ >+ private _isOperatorAnder(node) >+ { >+ const anderRegex = /^operator\&\.(.*?)$/; >+ return node.func instanceof NativeFunc && anderRegex.test(node.func.name); >+ } >+ >+ private _isOperatorGetter(node) >+ { >+ const getterRegex = /^operator.\.(.*?)$/; >+ return node.func instanceof NativeFunc && getterRegex.test(node.func.name); >+ } >+ >+ private _isOperatorIndexer(node) >+ { >+ return node.func instanceof NativeFunc && node.func.name == "operator&[]"; >+ } >+ >+ private _isOperatorSetter(node) >+ { >+ const setterRegex = /^operator\.(.*?)=$/; >+ return node.func instanceof NativeFunc && setterRegex.test(node.func.name) >+ } >+ >+ private _isOperatorVectorAccessor(node) >+ { >+ const regex = /^operator\.[xyzw]+$/g; >+ return node.func && node.func.implementationData instanceof SwizzleOp && regex.test(node.func.name); >+ } >+ >+ private _isOperatorCast(node: Func) >+ { >+ // FIXME: Why is this being passed a Func rather than a CallExpression? >+ return node instanceof NativeFunc && node.name == "operator cast"; >+ } >+ >+ private _isUnaryOperator(node) >+ { >+ const operatorRegex = /^operator\~$/; >+ return node instanceof NativeFunc && operatorRegex.test(node.name); >+ } >+ >+ private _isBinaryOperator(node) >+ { >+ const operatorRegex = /operator(\+|\-|\*|\/|\^|\&|\||\&\&|\|\||\<\<|\>\>|\<|\<\=|\>|\>\=|\=\=|\!\=)$/; >+ return node instanceof NativeFunc && operatorRegex.test(node.name); >+ } >+ >+ private _extractOperatorName(node) >+ { >+ return node.name.substring("operator".length); >+ } >+ >+ private _doNotProduceReferenceFromReferenceNode(node) >+ { >+ const emitter = this; >+ >+ class DefiniteRefVisitor extends Visitor { >+ >+ visitMakePtrExpression(node) >+ { >+ return Node.visit(node.lValue, emitter); >+ } >+ >+ visitCallExpression(node) >+ { >+ const anderRegex = /^operator\&\.(.*?)$/; >+ if (anderRegex.test(node.func.name)) >+ return emitter._handleFieldAccess(node); >+ else if (node.func.name === "operator&[]") >+ return emitter._handleIndexAccess(node); >+ } >+ } >+ >+ return Node.visit(node, new DefiniteRefVisitor()); >+ } >+ >+ visitVariableDecl(node) >+ { >+ let id = this._fresh(); >+ this._nameMap.set(node.name, id); >+ >+ this._add(`${new VarOrFieldDeclaration(this._typeNamer, node.type, id)}; // ${node.name}`); >+ >+ if (node.initializer) { >+ let expr = node.initializer.visit(this); >+ this._add(`${id} = ${expr};`); >+ } else >+ this._zeroInitialize(node.type, id); >+ } >+ >+ visitVariableRef(node) >+ { >+ if (node.variable instanceof AnonymousVariable) >+ return Node.visit(node.variable, this); >+ let name = node.name; >+ if (!this._nameMap.has(name)) >+ throw new Error(`${node.name} not found in this function's variable map`); >+ return this._nameMap.get(name); >+ } >+ >+ visitMakeArrayRefExpression(node) >+ { >+ const elemName = Node.visit(node.lValue, this); >+ const type = this._typeNamer.mslTypeName(node.type); >+ const id = this._fresh(); >+ this._add(`${type} ${id};`); >+ this._add(`${id}.length = ${node.numElements.value};`); >+ if (node.lValue.variable.type.isArray) >+ this._add(`${id}.ptr = ${elemName};`); >+ else >+ this._add(`${id}.ptr = &(${elemName});`); >+ return id; >+ } >+ >+ visitAnonymousVariable(node) >+ { >+ // Anonymous variable names are of the form anonVar<i> (where i is an integer), i.e. >+ // the set of anonymous variable names is disjoint from the set of definable variable names. >+ if (!this._nameMap.has(node.name)) { >+ let id = this._fresh(); >+ this._nameMap.set(node.name, id); >+ this._add(`${this._typeNamer.mslTypeName(node.type)} ${id}; // ${node.name}`); >+ this._zeroInitialize(node.type, id); >+ return id; >+ } else >+ return this._nameMap.get(node.name); >+ } >+ >+ visitAssignment(node) >+ { >+ const lhs = Node.visit(node.lhs, this); >+ const rhs = Node.visit(node.rhs, this); >+ this._add(`${lhs} = ${rhs};`); >+ return rhs; >+ } >+ >+ visitCommaExpression(node: CommaExpression) >+ { >+ let result; >+ for (let expr of node.list) >+ result = Node.visit(expr, this); >+ return result; >+ } >+ >+ visitCallExpression(node) >+ { >+ // Evaluate the arguments right-to-left. >+ let argIds = []; >+ for (let i = node.argumentList.length - 1; i >= 0; i--) { >+ let id = node.argumentList[i].visit(this); >+ argIds.unshift(id); >+ } >+ let result = this._fresh(); >+ if (node.func.returnType.name !== "void") { >+ this._add(`// Result variable for call ${node.func.name}(${argIds.join(", ")})`); >+ this._add(`${this._typeNamer.mslTypeName(node.resultType)} ${result};`); >+ } >+ this._makeFunctionCall(node.func, result, argIds); >+ >+ return result; >+ } >+ >+ private _makeFunctionCall(node, result, args) >+ { >+ if (!node) >+ throw new Error(`Can't compile call ${node} of non-native functions'`); >+ >+ if (this._isOperatorVectorAccessor(node)) >+ this._add(`${result} = ${args[0]}.${node.func.implementationData.components.join("")};`); >+ else if (this._isOperatorAnder(node)) { >+ this._add(`// ${node.name}`); >+ this._add(`${result} = &(${this._handleFieldAccess(node)});`); >+ } else if (node.implementationData instanceof BuiltinVectorGetter) >+ this._emitBuiltinVectorGetter(node, result, args); >+ else if (node.implementationData instanceof BuiltinVectorSetter) >+ this._emitBuiltinVectorSetter(node, result, args); >+ else if (node.implementationData instanceof BuiltinVectorIndexGetter) >+ this._emitBuiltinVectorIndexGetter(node, result, args); >+ else if (node.implementationData instanceof BuiltinVectorIndexSetter) >+ this._emitBuiltinVectorIndexSetter(node, result, args); >+ else if (node.implementationData instanceof BuiltinVectorEqualityOperator) >+ this._emitBuiltinVectorEquality(node, result, args); >+ else if (this._isOperatorSetter(node)) >+ this._add(`// TODO: (Native function) ${node.name}`); >+ else if (this._isOperatorGetter(node)) >+ this._add(`// TODO: (Native function) ${node.name}`); >+ else if (this._isOperatorIndexer(node)) >+ this._add(`// TODO: (Native function) ${node.name}`); >+ else if (this._isOperatorCast(node)) { >+ const retType = this._typeNamer.mslTypeName(node.returnType); >+ this._add(`${result} = ${retType}(${args.join(", ")});`); >+ } else if (this._isUnaryOperator(node)) { >+ const op = this._extractOperatorName(node); >+ this._add(`${result} = ${op}(${args[0]});`); >+ } else if (this._isBinaryOperator(node)) { >+ const op = this._extractOperatorName(node); >+ this._add(`${result} = ${args[0]} ${op} ${args[1]};`); >+ } else >+ throw new Error(`Unimplemented native function '${node.name}' at MSLStatementEmitter.makeFunctionCall`); >+ >+ return result; >+ } >+ >+ private _emitCallToMetalFunction(name, result, args) >+ { >+ this._add(`${result} = ${name}(${args.join(", ")});`); >+ } >+ >+ private _emitBuiltinVectorGetter(node, result, args) >+ { >+ this._add(`${result} = ${args[0]}.${node.implementationData.elementName};`); >+ } >+ >+ private _emitBuiltinVectorSetter(node, result, args) >+ { >+ this._add(`${result}.${node.implementationData.elementName} = ${args[1]};`); >+ } >+ >+ private _emitBuiltinVectorIndexSetter(node, result, args) >+ { >+ // FIXME: Remove this once the new standard library is checked in >+ // const vector = args[0]; >+ const vector = result; >+ const index = args[1]; >+ const value = args[2]; >+ const size = (<any>typeOf(node.parameters[0])).unifyNode.numElementsValue; >+ >+ if (size == 2) { >+ this._add(`if (${index} == 0)`); >+ this._add(` ${vector}.x = ${value};`); >+ this._add(`else if (${index} == 1)`); >+ this._add(` ${vector}.y = ${value};`); >+ this._add(`else {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ } else if (size == 3) { >+ this._add(`if (${index} == 0)`); >+ this._add(` ${vector}.x = ${value};`); >+ this._add(`else if (${index} == 1)`); >+ this._add(` ${vector}.y = ${value};`); >+ this._add(`else if (${index} == 2)`); >+ this._add(` ${vector}.z = ${value};`); >+ this._add(`else {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ } else if (size == 4) { >+ this._add(`if (${index} == 0)`); >+ this._add(` ${vector}.x = ${value};`); >+ this._add(`else if (${index} == 1)`); >+ this._add(` ${vector}.y = ${value};`); >+ this._add(`else if (${index} == 2)`); >+ this._add(` ${vector}.z = ${value};`); >+ this._add(`else if (${index} == 3)`); >+ this._add(` ${vector}.w = ${value};`); >+ this._add(`else {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ } else >+ throw new Error(`${node.parameters[0]} doesn't have a vector type.`); >+ } >+ >+ private _emitBuiltinVectorIndexGetter(node, result, args) >+ { >+ // FIXME: Remove this once the new standard library is checked in >+ const vector = args[0]; >+ const index = args[1]; >+ const size = (<any>typeOf(node.parameters[0])).unifyNode.numElementsValue; >+ >+ if (size == 2) { >+ this._add(`if (${index} == 0)`); >+ this._add(` ${result} = ${vector}.x;`); >+ this._add(`else if (${index} == 1)`); >+ this._add(` ${result} = ${vector}.y;`); >+ this._add(`else {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ } else if (size == 3) { >+ this._add(`if (${index} == 0)`); >+ this._add(` ${result} = ${vector}.x;`); >+ this._add(`else if (${index} == 1)`); >+ this._add(` ${result} = ${vector}.y;`); >+ this._add(`else if (${index} == 2)`); >+ this._add(` ${result} = ${vector}.z;`); >+ this._add(`else {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ } else if (size == 4) { >+ this._add(`if (${index} == 0)`); >+ this._add(` ${result} = ${vector}.x;`); >+ this._add(`else if (${index} == 1)`); >+ this._add(` ${result} = ${vector}.y;`); >+ this._add(`else if (${index} == 2)`); >+ this._add(` ${result} = ${vector}.z;`); >+ this._add(`else if (${index} == 3)`); >+ this._add(` ${result} = ${vector}.w;`); >+ this._add(`else {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ } else >+ throw new Error(`${node.parameters[0]} doesn't have a vector type.`); >+ } >+ >+ private _emitBuiltinVectorEquality(node, result, args) >+ { >+ const elementNames = [ "x", "y", "z", "w" ]; >+ const comps = []; >+ for (let i = 0; i < node.implementationData.size; i++) { >+ comps.push(`(${args[0]}.${elementNames[i]} == ${args[1]}.${elementNames[i]})`); >+ } >+ this._add(`${result} = ${comps.join(" && ")};`); >+ } >+ >+ private _handleFieldAccess(node) >+ { >+ const arg = node.argumentList[0]; >+ let field = node.func.implementationData.name; >+ const type = node.func.implementationData.type; >+ // FIXME: Why would this ever be null? >+ const typeAttributes = this._typeAttributes.attributesForType(type); >+ if (typeAttributes) >+ field = typeAttributes.mangledFieldName(field); >+ >+ if (arg instanceof MakePtrExpression) { >+ const argId = Node.visit(arg.lValue, this); >+ if (arg.lValue && arg.lValue instanceof VariableRef) >+ return `${argId}.${field}`; >+ else >+ return `(${argId}).${field}`; >+ } else >+ return `${Node.visit(arg, this)}->${field}`; >+ } >+ >+ private _handleIndexAccess(node) >+ { >+ const array = node.argumentList[0]; >+ const index = node.argumentList[1]; >+ >+ // FIXME: Avoid so much special casing. >+ >+ if (array instanceof MakeArrayRefExpression) { >+ if (index.isLiteral) { >+ // Type checking will hopefully have already ensured this... >+ if (index.value >= 0 && index.value < array.numElements.value) { >+ const variable = Node.visit(array.lValue, this); >+ return `${variable}[${index.value}]`; >+ } else >+ throw new Error(`Index out of bounds in ${node}`); >+ } else { >+ // TODO: It would be interesting to see what kind of BCE could be done here. >+ // Index checks aren't necessary for values < 0 because the type checker will >+ // have already ensured that the index value type is an unsigned integer. >+ const variable = Node.visit(array.lValue, this); >+ const indexId = Node.visit(index, this); >+ this._add(`if (${indexId} >= ${array.numElements.value}) {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add(`}`); >+ return `${variable}[${indexId}]`; >+ } >+ } else if (typeOf(array) instanceof ArrayRefType) { >+ const variable = array.visit(this); >+ const indexId = index.visit(this); >+ this._add(`if (${indexId} >= ${variable}.length) {`); >+ this._indent(); >+ this._emitReturnZeroValue(); >+ this._unindent(); >+ this._add('}'); >+ return `${variable}.ptr[${indexId}]`; >+ } else >+ this._add(`// TODO: Handle ${array} in MSLStatementEmitter._handleIndexAccess`); >+ } >+ >+ visitMakePtrExpression(node) >+ { >+ const expr = node.lValue.visit(this); >+ return `&(${expr})`; >+ } >+ >+ visitFunctionLikeBlock(node) >+ { >+ const canBeInlined = functionLikeBlockCanBeInlined(node); >+ if (canBeInlined) >+ this._add(`// inline ${node.origin.text}`); >+ else >+ this._add(`// call to ${node.origin.text}`); >+ >+ // 1. Create result variable if necessary. >+ let resultVar = null; >+ if (node.returnType.name != "void") { >+ resultVar = this._fresh(); >+ this._add(`${this._typeNamer.mslTypeName(node.returnType)} ${resultVar};`); >+ } >+ >+ this._add("{"); >+ this._indent(); >+ >+ // 2. Evaluate the arguments. >+ const args = []; >+ for (let i = node.argumentList.length - 1; i >= 0; i--) { >+ let id = node.argumentList[i].visit(this); >+ args.unshift(id); >+ } >+ >+ // 3. Emit the call inline or as a real call. >+ if (canBeInlined) >+ this._callFunctionLikeBlockInline(node, resultVar, args); >+ else >+ this._callFunctionLikeBlockWithCall(node, resultVar, args); >+ >+ this._unindent(); >+ this._add("}"); >+ >+ return resultVar; >+ } >+ >+ private _callFunctionLikeBlockInline(node, resultVar, args) >+ { >+ const nameMap = new Map(this._nameMap); >+ const params = node.parameters; >+ for (let i = 0; i < params.length; i++) >+ nameMap.set(params[i].name, args[i]); >+ >+ const emitter = new MSLStatementEmitter(this._funcMangler, this._typeNamer, node.body, nameMap, node.origin.text, this._lineNumbers, this._typeAttributes, resultVar, this._counter, this.ancestorReturnType); >+ this._addLines(emitter.lines); >+ } >+ >+ private _callFunctionLikeBlockWithCall(node, resultVar, args) >+ { >+ const mangledCallName = this._funcMangler.mangle(node.func); >+ this._add(`${resultVar} = ${mangledCallName}(${args.join(", ")});`); >+ } >+ >+ // 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: ForLoop) >+ { >+ this._emitAsLoop(node.condition, node.increment, (conditionVar) => { >+ Node.visit(node.initialization, this); >+ this._emitLoopCondition(); >+ this._add(`while (${conditionVar}) {`); >+ this._emitScoped(node.body); >+ this._emitLoopBodyEnd(); >+ this._add('}'); >+ }); >+ } >+ >+ visitWhileLoop(node) >+ { >+ this._emitAsLoop(node.conditional, null, (conditionVar) => { >+ this._add(`while (${conditionVar}) {`); >+ this._emitScoped(node.body); >+ this._emitLoopCondition(); >+ this._add('}'); >+ }); >+ } >+ >+ visitDoWhileLoop(node) >+ { >+ this._emitAsLoop(node.conditional, null, (conditionVar: string) => { >+ this._add("do {"); >+ this._emitScoped(node.body); >+ this._emitLoopBodyEnd(); >+ this._add(`} while (${conditionVar});`); >+ }); >+ } >+ >+ private _emitAsLoop(conditional: Node, increment: Node, emitLoopBody: (conditionalVar: string) => void) >+ { >+ 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(); >+ } >+ >+ private _emitLoopBodyEnd() >+ { >+ this._emitLoopIncrement(); >+ this._emitLoopCondition(); >+ } >+ >+ private _emitLoopIncrement() >+ { >+ if (this._loopIncrement) >+ this._emitScoped(this._loopIncrement); >+ } >+ >+ private _emitLoopCondition() >+ { >+ if (this._loopCondition) { >+ const conditionResult: Node = this._emitScoped(this._loopCondition); >+ this._add(`${this._loopConditionVariable} = ${conditionResult};`); >+ } >+ } >+ >+ // Switch statements. >+ >+ visitSwitchStatement(node) >+ { >+ const caseValueEmitter = new ConstexprEmitter(); >+ >+ 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 ${Node.visit(switchCase.value, caseValueEmitter)}: {`); >+ else >+ this._add("default: {"); >+ this._emitScoped(switchCase.body); >+ this._add("}"); >+ } >+ >+ this._unindent(); >+ this._add("}"); >+ } >+ >+ visitSwitchCase(node) >+ { >+ throw new Error(`// FIXME: Hit |visitSwitchCase|, should be handled by |visitSwitchStatement|`); >+ } >+ >+ // Literals >+ >+ visitBoolLiteral(node) >+ { >+ const fresh = this._fresh(); >+ this._add(`bool ${fresh} = ${node.value};`); >+ return fresh; >+ } >+ >+ visitEnumLiteral(node) >+ { >+ const fresh = this._fresh(); >+ this._add(`${this._typeNamer.mslTypeName(node.type.baseType)} ${fresh} = ${node.member.value};`); >+ return fresh; >+ } >+ >+ visitGenericLiteral(node) >+ { >+ const fresh = this._fresh(); >+ this._add(`${this._typeNamer.mslTypeName(node.type)} ${fresh} = ${node.value};`); >+ return fresh; >+ } >+ >+ visitNullLiteral(node) >+ { >+ return `(nullptr)`; >+ } >+ >+ visitDotExpression(node) >+ { >+ throw new Error(`Dot expressions ${node} should have been converted to an operator`); >+ } >+ >+ visitTrapStatement(node) >+ { >+ this._emitReturnZeroValue(); >+ } >+ >+ 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._unindent(); >+ this._add('} else {'); >+ this._indent(); >+ const right = node.right.visit(this); >+ this._add(`${result} = ${right};`); >+ this._unindent(); >+ this._add('}'); >+ } else if (node.text == "||") { >+ const left = node.left.visit(this); >+ this._add(`${result} = ${left};`); >+ this._add(`if (!${result}) {`); >+ this._indent(); >+ const right = node.right.visit(this); >+ this._add(`${result} = ${right};`); >+ this._unindent(); >+ this._add("}"); >+ } else >+ throw new Error(`Unrecognized logical expression ${node.text}`); >+ return result; >+ } >+ >+ // Unimplemented methods. I write out comments for these rather than throwing exceptions because it makes it easier for me >+ // to see where the nodes occur in the abstract syntax tree >+ >+ visitConvertPtrToArrayRefExpression(node) >+ { >+ this._add("// TODO: visitConvertPtrToArrayRefExpression"); >+ } >+ >+ visitIndexExpression(node) >+ { >+ this._add("// TODO: visitIndexExpression"); >+ } >+ >+ visitNativeFunc(node) >+ { >+ this._add("// TODO: visitNativeFunc"); >+ } >+ >+ visitNativeTypeInstance(node) >+ { >+ this._add("// TODO: visitNativeTypeInstance"); >+ } >+ >+ visitReadModifyWriteExpression(node) >+ { >+ this._add("// TODO: visitReadModifyWriteExpression"); >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeNamer.ts b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeNamer.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..c6174e97c2952bc9af7a4a204a796b5f4bc86a2e >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MSLTypeNamer.ts >@@ -0,0 +1,133 @@ >+/* >+ * 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 MSLTypeNamer { >+ private _typeUnifier: TypeUnifier; >+ >+ constructor(typeUnifier) >+ { >+ this._typeUnifier = typeUnifier; >+ } >+ >+ get typeUnifier() >+ { >+ return this._typeUnifier; >+ } >+ >+ uniqueTypeId(type) >+ { >+ return this.typeUnifier.uniqueTypeId(type); >+ } >+ >+ // Yields the string that should be used in MSL source for the type. >+ mslTypeName(type) >+ { >+ let name; >+ >+ const unifiedType = this.typeUnifier.unifyTypes(type); >+ if (!unifiedType) >+ throw new Error(`${type} was not unified`); >+ >+ const typeUnifier = this.typeUnifier; >+ const typeNamer = this; >+ >+ class TypeNameVisitor extends Visitor >+ { >+ visitNativeType(node) >+ { >+ 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" >+ }; >+ >+ name = nativeTypeNameMap[node.name]; >+ } >+ >+ visitStructType(node) >+ { >+ name = typeUnifier.uniqueTypeId(node); >+ } >+ >+ visitEnumType(node) >+ { >+ return Node.visit(node.baseType, this); >+ } >+ >+ visitReferenceType(node) >+ { >+ name = `${node.addressSpace} ${typeNamer.mslTypeName(node.elementType)}*`; >+ } >+ >+ visitArrayType(node) >+ { >+ name = `${typeNamer.mslTypeName(node.elementType)}*`; >+ } >+ >+ visitArrayRefType(node) >+ { >+ name = typeUnifier.uniqueTypeId(node); >+ } >+ >+ 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 support element type`); >+ >+ name = `${elementTypeName}${node.numElementsValue}`; >+ } >+ } >+ >+ unifiedType.visit(new TypeNameVisitor()); >+ >+ if (!name) >+ throw new Error(`${unifiedType} doesn't have a name`); >+ >+ return name; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/MetalCodegen.ts b/Tools/WebGPUShadingLanguageRI/Metal/MetalCodegen.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..7b5ff241a41ce4719bb2f10a0377807394c4c970 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/MetalCodegen.ts >@@ -0,0 +1,310 @@ >+/* >+ * 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 CompileResult 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 MetalCodegen { >+ private _program: Program; >+ private _lineNumbers: number[]; >+ private _declarations: any[]; >+ private _allowComments: boolean; >+ private _includeFunctionForwardDeclarations: boolean; >+ private _includeTypeReference: boolean; >+ private _typeUnifier: TypeUnifier; >+ private _typeNamer: MSLTypeNamer; >+ private _forwardTypeDecls: any[]; >+ private _typeDefinitions: any[]; >+ private _forwardFunctionDecls: any[]; >+ private _functionDefintions: MSLFunctionDefinition[]; >+ private _allTypeAttributes: FindTopLevelTypeAttributes; >+ private _funcNameMangler: NameMangler; >+ private _functionSources: Map<string, string>; >+ >+ constructor(program: Program, lineNos: number[] = null) >+ { >+ this._program = program; >+ this._lineNumbers = lineNos; >+ >+ this._declarations = []; >+ >+ this._allowComments = true; >+ this._includeFunctionForwardDeclarations = true; >+ this._includeTypeReference = true; >+ } >+ >+ get program() >+ { >+ return this._program; >+ } >+ >+ get declarations() >+ { >+ return this._declarations; >+ } >+ >+ get allowComments() >+ { >+ return this._allowComments; >+ } >+ >+ set allowComments(newAllowComments) >+ { >+ this._allowComments = newAllowComments; >+ } >+ >+ get includeTypeReference() >+ { >+ return this._includeTypeReference; >+ } >+ >+ set includeTypeReference(newValue) >+ { >+ this._includeTypeReference = newValue; >+ } >+ >+ get functionSources(): Map<string, string> >+ { >+ return this._functionSources; >+ } >+ >+ // The main entry point for the compiler, however clients should generally call whlslToMsl, >+ // which is defined at the bottom of this file. >+ generateMsl(): CompileResult >+ { >+ // try { >+ const src = this._tryGenerateMsl(); >+ const mangledMap = {}; >+ for (let func of this._functionDefintions) { >+ const key = func.func.name; >+ const value = func.mangledName; >+ if (!(key in mangledMap)) >+ mangledMap[key] = []; >+ mangledMap[key].push(value); >+ } >+ >+ return new CompileResult(src, null, mangledMap, this._functionSources); >+ // } catch (e) { >+ // return new CompileResult(null, e, null, null); >+ // } >+ } >+ >+ private _tryGenerateMsl(): string >+ { >+ this._typeUnifier = new TypeUnifier(); >+ this._typeNamer = new MSLTypeNamer(this._typeUnifier); >+ this._forwardTypeDecls = []; >+ this._typeDefinitions = []; >+ this._forwardFunctionDecls = []; >+ this._functionDefintions = []; >+ >+ // Step 1: Find all the functions that actually need to be compiled >+ // (including functions that need to be de-inlined). We additionally perform >+ // type unification. >+ >+ // FIXME: The naming here >+ const entryFuncDefs = this._findEntryPointDefinitions(); >+ const unifiedEntryFunctionDefs = this._unifyFunctionTypes(entryFuncDefs); >+ const unifiedFunctionDefs = this._findFunctionDefinitionsToCompile(unifiedEntryFunctionDefs); >+ >+ // Step 2: Find properties of the types and create type and function declarations. >+ >+ this._allTypeAttributes = new FindTopLevelTypeAttributes(unifiedFunctionDefs, this._typeUnifier); >+ this._createTypeDecls(); >+ this._createFunctionDecls(unifiedFunctionDefs); >+ >+ // Step 3: Generate the actual MSL source code. >+ >+ 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); >+ if (this._includeFunctionForwardDeclarations) >+ addSection("Forward function declarations", this._forwardFunctionDecls); >+ addSection("Function definitions", this._functionDefintions); >+ >+ // Step 4: Post-generation source tidy up (type reference comments and comment removal). >+ >+ if (this.includeTypeReference) { >+ outputStr += "#pragma mark - Type reference\n"; >+ for (let type of this._typeUnifier.allTypes) { >+ const typeId = this._typeUnifier.uniqueTypeId(type); >+ const typeName = this._typeUnifier.uniqueName(type); >+ outputStr += `\n// ${typeId} <- ${typeName}`; >+ } >+ } >+ >+ if (!this.allowComments) >+ outputStr = this._removeCommentsAndEmptyLines(outputStr); >+ >+ // Step 5: Create a map of the mangled function names to their source >+ // (used for the copy buttons). >+ this._functionSources = new Map(); >+ for (let func of this._functionDefintions) >+ this._functionSources.set(func.mangledName, func.toString()); >+ >+ return outputStr; >+ } >+ >+ private _findEntryPointDefinitions() >+ { >+ 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) >+ { >+ if (!(node.func instanceof NativeFunc)) { >+ if (!functions.has(node.func)) >+ node.func.visit(this); >+ functions.add(node.func); >+ } >+ } >+ } >+ const findFunctionsThatGetCalledVisitor = new FindFunctionsThatGetCalled(); >+ >+ for (let entryPoint of entryPointFunctions) >+ entryPoint.visit(findFunctionsThatGetCalledVisitor); >+ >+ return Array.from(functions); >+ } >+ >+ // FIXME: This has a pretty terrible name >+ private _unifyFunctionTypes(functionDefinitions) >+ { >+ this._funcNameMangler = new NameMangler('F'); >+ return functionDefinitions.map(f => { >+ const func = this._typeUnifier.unifyTypes(f); >+ return { >+ func: func, >+ mangledName: this._funcNameMangler.mangle(func) >+ } >+ }); >+ } >+ >+ // Finds FunctionLikeBlocks that can't be inlined because they don't have reducible control flow, so promotes them >+ // to full function definitions. >+ private _findFunctionDefinitionsToCompile(functionDefinitions) >+ { >+ const functionSet = new Set<Node>(); >+ const funcNameMangler = this._funcNameMangler; >+ >+ class FindFunctionsToDeclare extends Visitor { >+ visitFunctionLikeBlock(node: FunctionLikeBlock) >+ { >+ if (!functionSet.has(node.func)) { >+ if (!functionLikeBlockCanBeInlined(node)) { >+ functionSet.add(node.func); >+ functionDefinitions.push({ >+ func: node.func, >+ mangledName: funcNameMangler.mangle(node.func) >+ }); >+ } >+ super.visitFunctionLikeBlock(node); >+ } >+ } >+ } >+ >+ const visitor = new FindFunctionsToDeclare(); >+ >+ for (let funcDef of functionDefinitions) >+ Node.visit(funcDef.func, visitor); >+ >+ return functionDefinitions; >+ } >+ >+ private _createTypeDecls() >+ { >+ const typesThatNeedDeclaration = this._typeUnifier.typesThatNeedDeclaration(); >+ const typeDeclsInOrder = this._sortTypeDeclarationsInTopologicalOrder(typesThatNeedDeclaration); >+ >+ for (let type of typeDeclsInOrder) { >+ const metalStructCodeGen = new StructCodegen(type, this._typeNamer, this._lineNumbers, this._allTypeAttributes.attributesForType(type)); >+ this._forwardTypeDecls.push(metalStructCodeGen.forwardDeclaration); >+ this._typeDefinitions.push(metalStructCodeGen.finalDefinition); >+ } >+ } >+ >+ private _createFunctionDecls(unifiedFunctionDefs) >+ { >+ for (let func of unifiedFunctionDefs) { >+ this._forwardFunctionDecls.push(new MSLFunctionForwardDeclaration(this._funcNameMangler, func.func, this._typeNamer, func.mangledName, this._lineNumbers, this._allTypeAttributes)); >+ this._functionDefintions.push(new MSLFunctionDefinition(this._funcNameMangler, func.func, this._typeNamer, func.mangledName, this._lineNumbers, this._allTypeAttributes)); >+ } >+ } >+ >+ private _sortTypeDeclarationsInTopologicalOrder(typesToDeclare) >+ { >+ const declarations = new Array(); >+ for (let type of typesToDeclare) >+ declarations.push(type); >+ const typeOrder = new TypeSorter(this._typeUnifier, typesToDeclare).topologicalOrder; >+ const typeOrderMap = new Map<Type, number>(); >+ for (let i = 0; i < typeOrder.length; i++) >+ typeOrderMap.set(typeOrder[i], i); >+ return declarations.sort((a, b) => typeOrderMap.get(this._typeUnifier.uniqueTypeId(a)) - typeOrderMap.get(this._typeUnifier.uniqueTypeId(b))); >+ } >+ >+ // Also removes #pragma marks. >+ private _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'); >+ } >+} >+ >+// Main wrapper for the compiler. Clients should use this function to compile WHLSL. >+function whlslToMsl(src: string): CompileResult { >+ let parsedProgram; >+ try { >+ parsedProgram = prepare("/internal/test", 0, src); >+ } catch (e) { >+ return new CompileResult(null, e, null, null); >+ } >+ >+ if (!(parsedProgram instanceof Program)) >+ return new CompileResult(null, new Error("Compilation failed"), null, null); >+ >+ const compiler = new MetalCodegen(parsedProgram, lineNumbers(src)); >+ return compiler.generateMsl(); >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/NameMangler.ts b/Tools/WebGPUShadingLanguageRI/Metal/NameMangler.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..7d51184feef26ac5bc5fcd496ffc11d351848b24 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/NameMangler.ts >@@ -0,0 +1,44 @@ >+/* >+ * 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 NameMangler { >+ private _prefix: string; >+ private _counter: number; >+ private _mangledNameMap: Map<any, string>; >+ >+ constructor(prefix: string) >+ { >+ this._prefix = prefix; >+ this._counter = 0; >+ this._mangledNameMap = new Map(); >+ } >+ >+ mangle(key: any): string >+ { >+ 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/README.md b/Tools/WebGPUShadingLanguageRI/Metal/README.md >new file mode 100644 >index 0000000000000000000000000000000000000000..97cd86451de65695ad5b3b56338a81b721145093 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/README.md >@@ -0,0 +1,16 @@ >+# Metal Shader Language code generation >+ >+## Building >+ >+The MSL compiler is based around the existing WSL interpreter in the parent directory. The compiler is written in TypeScript, a typed superset of JavaScript. To compile and run the compiler: >+ >+1. Install [Node.js][Node] LTS >+2. Install [TypeScript][typescript] by running `npm install -g typescript` >+3. Either: >+ (a) Open Visual Studio Code, press CMD + SHIFT + B, click `build` or `watch` >+ (b) Run `tsc -p tsconfig.js` inside this directory >+4. Start a web browser in the parent directory >+5. Go to [http://localhost:8000/Metal][] to run the compiler >+ >+[Node]: https://nodejs.org >+[typescript]: https://www.typescriptlang.org >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/StructCodegen.ts b/Tools/WebGPUShadingLanguageRI/Metal/StructCodegen.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..2160084955c8955d0bc34d60780ffb8b5e1b57dc >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/StructCodegen.ts >@@ -0,0 +1,67 @@ >+/* >+ * 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 StructCodegen extends Visitor { >+ private _node: StructType; >+ private _typeNamer: MSLTypeNamer; >+ private _lineNumbers: number[]; >+ private _typeAttributes: TypeAttributes; >+ >+ private _forwardDeclaration: StructForwardDeclaration; >+ private _finalDefinition: StructFinalDefinition | ArrayRefDefinition; >+ >+ constructor(node: StructType, typeNamer, lineNos, typeAttributes) >+ { >+ super(); >+ this._node = node; >+ this._typeNamer = typeNamer; >+ this._lineNumbers = lineNos; >+ this._typeAttributes = typeAttributes; >+ >+ Node.visit(node, this); >+ } >+ >+ get forwardDeclaration() >+ { >+ return this._forwardDeclaration; >+ } >+ >+ get finalDefinition() >+ { >+ return this._finalDefinition; >+ } >+ >+ visitStructType(node) >+ { >+ this._forwardDeclaration = new StructForwardDeclaration(node, this._typeNamer, this._lineNumbers); >+ this._finalDefinition = new StructFinalDefinition(node, this._typeNamer, this._lineNumbers, this._typeAttributes); >+ } >+ >+ visitArrayRefType(node) >+ { >+ this._forwardDeclaration = new StructForwardDeclaration(node, this._typeNamer, this._lineNumbers); >+ this._finalDefinition = new ArrayRefDefinition(node, this._typeNamer); >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/StructFinalDefinition.ts b/Tools/WebGPUShadingLanguageRI/Metal/StructFinalDefinition.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..5c834e6c8f4e1778d23fd55d7361ee3aee88a06c >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/StructFinalDefinition.ts >@@ -0,0 +1,87 @@ >+/* >+ * 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 StructFinalDefinition { >+ private _structType: StructType; >+ private _typeNamer: MSLTypeNamer; >+ private _lineNumbers: number[]; >+ private _typeAttributes: TypeAttributes; >+ >+ constructor(structType: StructType, typeNamer: MSLTypeNamer, lineNos: number[], typeAttributes: TypeAttributes) >+ { >+ this._structType = structType; >+ this._typeNamer = typeNamer; >+ this._lineNumbers = lineNos; >+ this._typeAttributes = typeAttributes; >+ } >+ >+ get structType() >+ { >+ return this._structType; >+ } >+ >+ toString() >+ { >+ let src = ""; >+ if (this._structType.name) { >+ src += `// ${this._structType.name}`; >+ if (this._lineNumbers && this._structType.origin && this._lineNumbers[this._structType.origin.index]) >+ src += ` @ line ${this._lineNumbers[this._structType.origin.index]}`; >+ src += "\n"; >+ } >+ src += `struct ${this._typeNamer.uniqueTypeId(this._structType)} {\n`; >+ >+ let index = 0; >+ for (let [fieldName,field] of this._structType.fieldMap) { >+ let addAttribute = true; >+ const typeName = this._typeNamer.mslTypeName(field.type); >+ const mangledFieldName = this._typeAttributes.mangledFieldName(fieldName); >+ src += ` ${new VarOrFieldDeclaration(this._typeNamer, field.type, mangledFieldName)}`; >+ >+ const annotations = []; >+ >+ // FIXME: These aren't in the spec yet but are needed for the compiled code to work. >+ if (this._typeAttributes.isVertexAttribute) >+ annotations.push(`attribute(${index++})`); >+ >+ if (this._typeAttributes.isVertexOutputOrFragmentInput && fieldName === "wsl_Position") >+ annotations.push("position"); >+ >+ if (this._typeAttributes.isFragmentOutput && fieldName === "wsl_Color") { >+ // Argument is the color attachment index; we're currently only using 0. >+ annotations.push("color(0)"); >+ } >+ >+ if (annotations.length) >+ src += ` [[${annotations.join(", ")}]]`; >+ >+ src += ";\n"; >+ } >+ >+ src += "};"; >+ >+ return src; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/StructForwardDeclaration.ts b/Tools/WebGPUShadingLanguageRI/Metal/StructForwardDeclaration.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..80a4b7682d9ae249f1ead1e97157dd7f1b7f3f8a >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/StructForwardDeclaration.ts >@@ -0,0 +1,56 @@ >+/* >+ * 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 StructForwardDeclaration { >+ private _structType: StructType; >+ private _typeNamer: MSLTypeNamer; >+ private _lineNumbers: number[]; >+ >+ constructor(structType: StructType, typeNamer: MSLTypeNamer, lineNos: number[]) >+ { >+ this._structType = structType; >+ this._typeNamer = typeNamer; >+ this._lineNumbers = lineNos; >+ } >+ >+ get structType() >+ { >+ return this._structType; >+ } >+ >+ toString() >+ { >+ let str = `struct ${this._typeNamer.uniqueTypeId(this._structType)};`; >+ if (this._structType.name) { >+ str += ` // ${this._structType.name}`; >+ if (this._lineNumbers && this._structType.origin && this._lineNumbers[this._structType.origin.index]) >+ str += ` @ line ${this._lineNumbers[this._structType.origin.index]}`; >+ >+ } else >+ str += ` // ${this._structType}`; >+ >+ return str; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/Token.ts b/Tools/WebGPUShadingLanguageRI/Metal/Token.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..429a3ae57e4b7d72ab82d494a4c2cd258321e1ab >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/Token.ts >@@ -0,0 +1,46 @@ >+/* >+ * 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. >+ */ >+ >+// The output of MSLLexer. >+class Token { >+ private _type: string; >+ private _src: string; >+ >+ constructor(type, src) >+ { >+ this._type = type; >+ this._src = src; >+ } >+ >+ get type(): string >+ { >+ return this._type; >+ } >+ >+ get src(): string >+ { >+ return this._src; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/TypeAttributes.ts b/Tools/WebGPUShadingLanguageRI/Metal/TypeAttributes.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..467d47e56d51ca4a1695a3ac0e9c79ad050d52f7 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/TypeAttributes.ts >@@ -0,0 +1,91 @@ >+/* >+ * 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 TypeAttributes { >+ private _type: any; >+ private _isVertexAttribute: boolean; >+ private _isVertexOutputOrFragmentInput: boolean; >+ private _isFragmentOutput: boolean; >+ private _fieldMangler: NameMangler; >+ >+ constructor(type) >+ { >+ this._type = type; >+ this._isVertexAttribute = false; >+ this._isVertexOutputOrFragmentInput = false; >+ this._isFragmentOutput = false; >+ this._fieldMangler = new NameMangler('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: string) >+ { >+ return this.fieldMangler.mangle(fieldName); >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/TypeOfExpression.ts b/Tools/WebGPUShadingLanguageRI/Metal/TypeOfExpression.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..15226a2c43acc5261ef2870143656ac8639edf3c >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/TypeOfExpression.ts >@@ -0,0 +1,318 @@ >+/* >+ * 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. >+ */ >+ >+function typeOf(node: Node): Type { >+ class TypeVisitor extends Visitor { >+ >+ visitAnonymousVariable(node) >+ { >+ return node.type; >+ } >+ >+ visitArrayRefType(node) >+ { >+ return node; >+ } >+ >+ visitArrayType(node) >+ { >+ return node; >+ } >+ >+ visitAssignment(node) >+ { >+ return node.type; >+ } >+ >+ visitCallExpression(node) >+ { >+ return Node.visit(node.func, this); >+ } >+ >+ visitCommaExpression(node) >+ { >+ return Node.visit(node.list[node.list.length - 1], this); >+ } >+ >+ visitDotExpression(node) >+ { >+ return node.struct.fieldMap.get(node.fieldName).type; >+ } >+ >+ visitElementalType(node) >+ { >+ return node; >+ } >+ >+ visitEnumType(node) >+ { >+ return node; >+ } >+ >+ visitField(node) >+ { >+ return node.type; >+ } >+ >+ visitFunc(node) >+ { >+ return node.returnType; >+ } >+ >+ visitFuncDef(node) >+ { >+ return node.returnType; >+ } >+ >+ visitFuncParameter(node) >+ { >+ return node.type; >+ } >+ >+ visitFunctionLikeBlock(node) >+ { >+ return node.returnType; >+ } >+ >+ visitGenericLiteralType(node) >+ { >+ return node; >+ } >+ >+ visitIdentityExpression(node) >+ { >+ return Node.visit(node.target, this); >+ } >+ >+ visitIndexExpression(node) >+ { >+ return Node.visit(node.array.elementType, this); >+ } >+ >+ visitLogicalExpression(node) >+ { >+ throw new Error("TODO: Implement TypeVisitor.visitLogicalExpression"); >+ } >+ >+ visitLogicalNot(node) >+ { >+ return Node.visit(node.operand, this); >+ } >+ >+ visitMakeArrayRefExpression(node) >+ { >+ return node.type; >+ } >+ >+ visitMakePtrExpression(node) >+ { >+ throw new Error("TODO: Implement TypeVisitor.visitMakePtrExpression"); >+ } >+ >+ visitNativeFunc(node) >+ { >+ return node.returnType; >+ } >+ >+ visitNativeFuncInstance(node) >+ { >+ return node.returnType; >+ } >+ >+ visitNativeType(node) >+ { >+ return node; >+ } >+ >+ visitNativeTypeInstance(node) >+ { >+ return node; >+ } >+ >+ visitNullLiteral(node) >+ { >+ return node.type; >+ } >+ >+ visitNullType(node) >+ { >+ return node; >+ } >+ >+ visitPtrType(node) >+ { >+ return node; >+ } >+ >+ visitReadModifyWriteExpression(node) >+ { >+ throw new Error("TODO: Implement TypeVisitor.visitReadModifyWriteExpression"); >+ } >+ >+ visitReferenceType(node) >+ { >+ return node; >+ } >+ >+ visitStructType(node) >+ { >+ return node; >+ } >+ >+ visitTypeDef(node) >+ { >+ return node; >+ } >+ >+ visitTypeRef(node) >+ { >+ return node; >+ } >+ >+ visitTypeVariable(node) >+ { >+ return node; >+ } >+ >+ visitVariableDecl(node) >+ { >+ return node.type; >+ } >+ >+ visitVariableRef(node) >+ { >+ return node.variable.type; >+ } >+ >+ 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"); >+ } >+ >+ visitConvertPtrToArrayRefExpression(node) >+ { >+ throw new Error("ConvertPtrToArrayRefExpression has no type"); >+ } >+ >+ visitDereferenceExpression(node) >+ { >+ return node.type; >+ } >+ >+ 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) >+ { >+ throw new Error("GenericLiteral has no type"); >+ } >+ 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(node, new TypeVisitor()); >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/TypeSorter.ts b/Tools/WebGPUShadingLanguageRI/Metal/TypeSorter.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..a57ed8f08814e6314d00c9b3d53eb3fd37e27671 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/TypeSorter.ts >@@ -0,0 +1,70 @@ >+/* >+ * 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. >+ */ >+ >+// This class finds the topological order for type declarations. >+class TypeSorter extends Visitor { >+ private _typeUnifier: TypeUnifier; >+ private _visitedSet: Set<string>; >+ private _visitedOrder: Type[]; >+ >+ constructor(typeUnifier, types) >+ { >+ super(); >+ this._typeUnifier = typeUnifier; >+ this._visitedSet = new Set(); >+ this._visitedOrder = []; >+ >+ for (let type of types) >+ type.visit(this); >+ } >+ >+ get topologicalOrder() >+ { >+ return this._visitedOrder.slice().reverse(); >+ } >+ >+ visitEnumType(node) >+ { >+ const id = this._typeUnifier.uniqueTypeId(node); >+ if (!this._visitedSet.has(id)) >+ this._visitedOrder.push(node); >+ this._visitedSet.add(id); >+ } >+ >+ visitStructType(node) >+ { >+ const id = this._typeUnifier.uniqueTypeId(node); >+ if (!this._visitedSet.has(id)) { >+ this._visitedOrder.push(node); >+ super.visitStructType(node); >+ } >+ this._visitedSet.add(id); >+ } >+ >+ visitReferenceType(node) >+ { >+ // An empty implementation ensures that we don't recurse into the element type and needlessly create dependencies. >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/TypeUnifier.ts b/Tools/WebGPUShadingLanguageRI/Metal/TypeUnifier.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..d2a7e7bc7903c1c6241c59a4120a4095a8d4777c >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/TypeUnifier.ts >@@ -0,0 +1,251 @@ >+/* >+ * 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. >+ */ >+ >+// Replaces equivalent (but not identical) type instances with identical >+// instances. The SPIR-V compiler depended on toString and allowed for >+// TypeRef instances in the the Type tree. This class replaces each type >+// that it discovers with a unique equivalent instance and collapses >+// TypeRef instances by instantiating them where necessary. In order to >+// determine equivalence it uses a separate stringification algorithm (so >+// that we don't depend on the implementation of toString). >+class TypeUnifier extends Rewriter { >+ >+ private _nativeTypes: Map<any, any>; >+ private _enumTypes: Map<any, any>; >+ private _typeRefs: Map<any, any>; >+ private _structTypes: Map<any, any>; >+ private _arrayTypes: Map<any, any>; >+ private _arrayRefTypes: Map<any, any>; >+ private _pointerTypes: Map<any, any>; >+ private _allTypes: Set<any>; >+ private _typeUniqueID: Map<any, any>; >+ >+ constructor() >+ { >+ super(); >+ this._nativeTypes = new Map(); >+ this._enumTypes = new Map(); >+ this._typeRefs = new Map(); >+ this._structTypes = new Map(); >+ this._arrayTypes = new Map(); >+ this._arrayRefTypes = new Map(); >+ this._pointerTypes = new Map(); >+ this._allTypes = new Set(); >+ this._typeUniqueID = new Map(); >+ } >+ >+ get allTypes() >+ { >+ return this._allTypes; >+ } >+ >+ unifyTypes(node) >+ { >+ return node.visit(this); >+ } >+ >+ uniqueTypeId(type) >+ { >+ return this._typeUniqueID.get(this.uniqueName(this.unifyTypes(type))); >+ } >+ >+ // The Rewriter class doesn't recurse in the desired way, hence the overload. >+ visitFuncDef(node) >+ { >+ node._returnType = node.returnType.visit(this); >+ node._parameters = node.parameters.map(param => param.visit(this)); >+ node._body = node.body.visit(this); >+ return node; >+ } >+ >+ // The Rewriter class doesn't recurse in the desired way, hence the overload. >+ visitFunc(node) >+ { >+ if (node.typeParameters.length) >+ throw new Error(`Cannot unify types of polymorphic function ${node}`); >+ node._returnType = node.returnType.visit(this); >+ node._parameters = node.parameters.map(param => param.visit(this)); >+ return node; >+ } >+ >+ visitCallExpression(node) >+ { >+ let result = new CallExpression( >+ node.origin, node.name, >+ node.argumentList.map(argument => Node.visit(argument, this))); >+ >+ // FIXME: Why would this ever be null? >+ let argumentTypes = node.argumentTypes; >+ if (argumentTypes) >+ result.argumentTypes = argumentTypes.map(argumentType => argumentType.visit(this)); >+ >+ result.func = node.func.visit(this); >+ >+ // The possible overloads can contain type variables; this is OK because we never visit them. >+ result.possibleOverloads = node.possibleOverloads; >+ >+ if (node.isCast) >+ result.setCastData(node.returnType.visit(this)); >+ >+ result.resultType = Node.visit(node.resultType, this); >+ result.resultEPtr = node.resultEPtr; >+ >+ return result; >+ } >+ >+ visitTypeRef(node) >+ { >+ return node.type.visit(this); >+ } >+ >+ _createTypeIfNotExists(map, value) >+ { >+ const key = this.uniqueName(value); >+ if (map.has(key)) >+ return map.get(key); >+ else { >+ this._allTypes.add(value); >+ map.set(key, value); >+ this._typeUniqueID.set(key, `T${this._typeUniqueID.size}`); >+ return value; >+ } >+ } >+ >+ // Used for determing equivalence of type instances that do not include TypeRef, i.e. fully concrete types. >+ uniqueName(type) >+ { >+ class TypeUniqueNamer extends Visitor { >+ >+ visitNativeType(node) >+ { >+ return node.name; >+ } >+ >+ visitPtrType(node) >+ { >+ return `${node.elementType.visit(this)}* ${node.addressSpace}`; >+ } >+ >+ visitArrayType(node) >+ { >+ return `${node.elementType.visit(this)}[${node.numElements}]`; >+ } >+ >+ visitArrayRefType(node) >+ { >+ return `${node.elementType.visit(this)}[] ${node.addressSpace}`; >+ } >+ >+ visitEnumType(node) >+ { >+ return `enum ${node.name} : ${node.baseType.visit(this)}`; >+ } >+ >+ visitStructType(node) >+ { >+ let fields = []; >+ for (let field of node.fields) >+ fields.push(`${field.type.visit(this)} ${field.name}`); >+ >+ return `struct ${node.name} { ${fields.join("; ")} }`; >+ } >+ >+ visitVectorType(node) >+ { >+ return `${node.elementType.visit(this)}${node.numElements}`; >+ } >+ } >+ >+ return type.visit(new TypeUniqueNamer()); >+ } >+ >+ visitStructType(node) >+ { >+ const value = new StructType(node.origin, node.name, []); >+ for (let field of node.fields) >+ value.add(new Field(field.origin, field.name, field.type.visit(this))); >+ >+ return this._createTypeIfNotExists(this._structTypes, value); >+ } >+ >+ visitEnumType(node) >+ { >+ const baseType = node.baseType.visit(this); >+ const value = new EnumType(node.origin, node.name, baseType); >+ return this._createTypeIfNotExists(this._enumTypes, value); >+ } >+ >+ visitNativeType(node) >+ { >+ return this._createTypeIfNotExists(this._nativeTypes, node); >+ } >+ >+ visitArrayType(node) >+ { >+ const elementType = node.elementType.visit(this); >+ const value = new ArrayType(node.origin, elementType, node.numElements); >+ return this._createTypeIfNotExists(this._arrayTypes, value); >+ } >+ >+ visitArrayRefType(node) >+ { >+ const elementType = node.elementType.visit(this); >+ const value = new ArrayRefType(node.origin, node.addressSpace, elementType); >+ return this._createTypeIfNotExists(this._arrayRefTypes, value); >+ } >+ >+ visitPtrType(node) >+ { >+ const elementType = node.elementType.visit(this); >+ const value = new PtrType(node.origin, node.addressSpace, elementType); >+ return this._createTypeIfNotExists(this._pointerTypes, value); >+ } >+ >+ typesThatNeedDeclaration() >+ { >+ const declSet = new Set(); >+ for (let type of this._allTypes) { >+ let needsDecl = false; >+ const typeUnifier = this; >+ class NeedsDeclaration extends Visitor >+ { >+ visitElementalType(node) {} >+ visitStructType(node) >+ { >+ needsDecl = true; >+ } >+ >+ visitArrayRefType(node) >+ { >+ needsDecl = true; >+ } >+ } >+ type.visit(new NeedsDeclaration()); >+ if (needsDecl) >+ declSet.add(type); >+ } >+ return declSet; >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/VarOrFieldDeclaration.ts b/Tools/WebGPUShadingLanguageRI/Metal/VarOrFieldDeclaration.ts >new file mode 100644 >index 0000000000000000000000000000000000000000..27a2c1e85e3a7ac78594e856fec8ab90478d2815 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/VarOrFieldDeclaration.ts >@@ -0,0 +1,68 @@ >+/* >+ * 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. >+ */ >+ >+// The type namer doesn't handle arrays in the style needed for declarations >+// because in MSL array declarations are of the form T x[n] --- I don't believe >+// that there is a way of keeping the whole of the type on the left. >+class VarOrFieldDeclaration { >+ private _typeNamer: MSLTypeNamer; >+ private _type: Type; >+ private _name: string; >+ >+ constructor(typeNamer, type, name) >+ { >+ this._typeNamer = typeNamer; >+ this._type = type; >+ this._name = name; >+ } >+ >+ get typeNamer() >+ { >+ return this._typeNamer; >+ } >+ >+ get type() >+ { >+ return this._type; >+ } >+ >+ get name() >+ { >+ return this._name; >+ } >+ >+ toString() >+ { >+ if (this.type.isArray) { >+ const arrayType = <ArrayType>this.type; >+ const typeName = this.typeNamer.mslTypeName(arrayType.elementType); >+ const numElements = arrayType.numElements.value; >+ return `${typeName} ${this.name}[${numElements}]`; >+ } else { >+ const typeName = this.typeNamer.mslTypeName(this.type); >+ return `${typeName} ${this.name}`; >+ } >+ } >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/index.html b/Tools/WebGPUShadingLanguageRI/Metal/index.html >new file mode 100644 >index 0000000000000000000000000000000000000000..6097ca12b9c27a80206e5b1d6955006c2f031a4e >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/index.html >@@ -0,0 +1,525 @@ >+<!DOCTYPE html> >+<html> >+<head> >+ <title>WebGPU Shading Language (WHLSL) → Metal Shading Language</title> >+ <script src="../Node.js"></script> >+ <script src="../Type.js"></script> >+ <script src="../ReferenceType.js"></script> >+ <script src="../Value.js"></script> >+ <script src="../Expression.js"></script> >+ <script src="../Rewriter.js"></script> >+ <script src="../Visitor.js"></script> >+ <script src="../CreateLiteral.js"></script> >+ <script src="../CreateLiteralType.js"></script> >+ <script src="../PropertyAccessExpression.js"></script> >+ <script src="../SwizzleOp.js"></script> >+ <script src="../NativeType.js"></script> >+ >+ <script src="../AddressSpace.js"></script> >+ <script src="../AnonymousVariable.js"></script> >+ <script src="../ArrayRefType.js"></script> >+ <script src="../ArrayType.js"></script> >+ <script src="../Assignment.js"></script> >+ <script src="../AutoWrapper.js"></script> >+ <script src="../Block.js"></script> >+ <script src="../BoolLiteral.js"></script> >+ <script src="../Break.js"></script> >+ <script src="../BuiltinVectorConstructors.js"></script> >+ <script src="../BuiltinVectorGetter.js"></script> >+ <script src="../BuiltinVectorSetter.js"></script> >+ <script src="../BuiltinVectorIndexGetter.js"></script> >+ <script src="../BuiltinVectorIndexSetter.js"></script> >+ <script src="../BuiltinVectorEqualityOperator.js"></script> >+ <script src="../CallExpression.js"></script> >+ <script src="../CallFunction.js"></script> >+ <script src="../Check.js"></script> >+ <script src="../CheckLiteralTypes.js"></script> >+ <script src="../CheckLoops.js"></script> >+ <script src="../CheckRecursion.js"></script> >+ <script src="../CheckRecursiveTypes.js"></script> >+ <script src="../CheckReturns.js"></script> >+ <script src="../CheckUnreachableCode.js"></script> >+ <script src="../CheckTypesWithArguments.js"></script> >+ <script src="../CheckWrapped.js"></script> >+ <script src="../Checker.js"></script> >+ <script src="../CloneProgram.js"></script> >+ <script src="../CommaExpression.js"></script> >+ <script src="../ConstexprFolder.js"></script> >+ <script src="../Continue.js"></script> >+ <script src="../ConvertPtrToArrayRefExpression.js"></script> >+ <script src="../DoWhileLoop.js"></script> >+ <script src="../DotExpression.js"></script> >+ <script src="../DereferenceExpression.js"></script> >+ <script src="../EArrayRef.js"></script> >+ <script src="../EBuffer.js"></script> >+ <script src="../EBufferBuilder.js"></script> >+ <script src="../EPtr.js"></script> >+ <script src="../EnumLiteral.js"></script> >+ <script src="../EnumMember.js"></script> >+ <script src="../EnumType.js"></script> >+ <script src="../EvaluationCommon.js"></script> >+ <script src="../Evaluator.js"></script> >+ <script src="../ExpressionFinder.js"></script> >+ <script src="../ExternalOrigin.js"></script> >+ <script src="../Field.js"></script> >+ <script src="../FindHighZombies.js"></script> >+ <script src="../FlattenedStructOffsetGatherer.js"></script> >+ <script src="../FloatLiteral.js"></script> >+ <script src="../FloatLiteralType.js"></script> >+ <script src="../FoldConstexprs.js"></script> >+ <script src="../ForLoop.js"></script> >+ <script src="../Func.js"></script> >+ <script src="../FuncDef.js"></script> >+ <script src="../FuncParameter.js"></script> >+ <script src="../FunctionLikeBlock.js"></script> >+ <script src="../HighZombieFinder.js"></script> >+ <script src="../IdentityExpression.js"></script> >+ <script src="../IfStatement.js"></script> >+ <script src="../IndexExpression.js"></script> >+ <script src="../InferTypesForCall.js"></script> >+ <script src="../Inline.js"></script> >+ <script src="../Inliner.js"></script> >+ <script src="../IntLiteral.js"></script> >+ <script src="../IntLiteralType.js"></script> >+ <script src="../Intrinsics.js"></script> >+ <script src="../LateChecker.js"></script> >+ <script src="../Lexer.js"></script> >+ <script src="../LexerToken.js"></script> >+ <script src="../LiteralTypeChecker.js"></script> >+ <script src="../LogicalExpression.js"></script> >+ <script src="../LogicalNot.js"></script> >+ <script src="../LoopChecker.js"></script> >+ <script src="../MakeArrayRefExpression.js"></script> >+ <script src="../MakePtrExpression.js"></script> >+ <script src="../NameContext.js"></script> >+ <script src="../NameFinder.js"></script> >+ <script src="../NameResolver.js"></script> >+ <script src="../NativeFunc.js"></script> >+ <script src="../NormalUsePropertyResolver.js"></script> >+ <script src="../NullLiteral.js"></script> >+ <script src="../NullType.js"></script> >+ <script src="../OperatorAnderIndex.js"></script> >+ <script src="../OperatorArrayRefLength.js"></script> >+ <script src="../OperatorBool.js"></script> >+ <script src="../OriginKind.js"></script> >+ <script src="../OverloadResolutionFailure.js"></script> >+ <script src="../Parse.js"></script> >+ <script src="../Prepare.js"></script> >+ <script src="../PropertyResolver.js"></script> >+ <script src="../Program.js"></script> >+ <script src="../ProgramWithUnnecessaryThingsRemoved.js"></script> >+ <script src="../PtrType.js"></script> >+ <script src="../ReadModifyWriteExpression.js"></script> >+ <script src="../RecursionChecker.js"></script> >+ <script src="../RecursiveTypeChecker.js"></script> >+ <script src="../ResolveNames.js"></script> >+ <script src="../ResolveOverloadImpl.js"></script> >+ <script src="../ResolveProperties.js"></script> >+ <script src="../ResolveTypeDefs.js"></script> >+ <script src="../Return.js"></script> >+ <script src="../ReturnChecker.js"></script> >+ <script src="../ReturnException.js"></script> >+ <script src="../StandardLibrary.js"></script> >+ <script src="../StatementCloner.js"></script> >+ <script src="../StructLayoutBuilder.js"></script> >+ <script src="../StructType.js"></script> >+ <script src="../SwitchCase.js"></script> >+ <script src="../SwitchStatement.js"></script> >+ <script src="../SynthesizeArrayOperatorLength.js"></script> >+ <script src="../SynthesizeEnumFunctions.js"></script> >+ <script src="../SynthesizeStructAccessors.js"></script> >+ <script src="../SynthesizeOperatorBool.js"></script> >+ <script src="../SynthesizeCopyConstructorOperator.js"></script> >+ <script src="../SynthesizeDefaultConstructorOperator.js"></script> >+ <script src="../TernaryExpression.js"></script> >+ <script src="../TrapStatement.js"></script> >+ <script src="../TypeDef.js"></script> >+ <script src="../TypeDefResolver.js"></script> >+ <script src="../TypeRef.js"></script> >+ <script src="../TypeOverloadResolutionFailure.js"></script> >+ <script src="../TypedValue.js"></script> >+ <script src="../UintLiteral.js"></script> >+ <script src="../UintLiteralType.js"></script> >+ <script src="../UnificationContext.js"></script> >+ <script src="../UnreachableCodeChecker.js"></script> >+ <script src="../VariableDecl.js"></script> >+ <script src="../VariableRef.js"></script> >+ <script src="../VectorType.js"></script> >+ <script src="../VisitingSet.js"></script> >+ <script src="../WSyntaxError.js"></script> >+ <script src="../WTrapError.js"></script> >+ <script src="../WTypeError.js"></script> >+ <script src="../WhileLoop.js"></script> >+ <script src="../WrapChecker.js"></script> >+ >+ <script src="compiler.js"></script> >+ >+<link rel="stylesheet" type="text/css" href="metal.css" /> >+ >+<script> >+let defaultShaderSource = `struct VertexInput { >+ float2 position; >+ float3 color; >+} >+ >+struct VertexOutput { >+ float4 wsl_Position; >+ float3 color; >+} >+ >+struct FragmentOutput { >+ float4 wsl_Color; >+} >+ >+vertex VertexOutput vertexShader(VertexInput vertexInput) { >+ VertexOutput result; >+ result.wsl_Position = float4(vertexInput.position, 0., 1.); >+ result.color = vertexInput.color; >+ return result; >+} >+ >+fragment FragmentOutput fragmentShader(VertexOutput stageIn) { >+ FragmentOutput result; >+ result.wsl_Color = float4(stageIn.color, 1.); >+ return result; >+}`; >+ >+let whlslContainer; >+let mslContainer; >+ >+let whlslSourceView; >+let mslSourceView; >+ >+const LocalStorageWhlslKeyName = "ShaderSource"; >+const LocalStorageMslKeyName = "ShaderMSLResult"; >+ >+window.addEventListener("load", () => { >+ mslContainer = document.getElementById("MetalOutput"); >+ mslSourceView = new SourceView("MSL (previous compilation)", mslContainer, (src) => new MSLLexer(src).tokens); >+ >+ try { >+ const localStorage = window.localStorage; >+ const mslSource = localStorage.getItem(LocalStorageMslKeyName); >+ mslSourceView.src = mslSource; >+ } catch(e) { >+ } >+ >+ whlslContainer = document.getElementById("WHLSLInput"); >+ whlslSourceView = new SourceView("WHLSL", whlslContainer, lexWHLSL, compileWHLSL, true); >+ >+ try { >+ const localStorage = window.localStorage; >+ let whlslSource = localStorage.getItem(LocalStorageWhlslKeyName); >+ if (!whlslSource) >+ whlslSource = defaultShaderSource; >+ whlslSourceView.src = whlslSource; >+ whlslSourceView.compile(); >+ } catch (e) { >+ console.log(e); >+ } >+}); >+ >+function compileWHLSL(src) { >+ window.localStorage.setItem(LocalStorageWhlslKeyName, src); >+ const compileResult = whlslToMsl(src); >+ >+ if (compileResult.didSucceed) { >+ mslSourceView.src = compileResult.metalShaderLanguageSource; >+ mslSourceView.name = "MSL"; >+ >+ const copyMap = compileResult.functionSources; >+ copyMap.set("MSL", compileResult.metalShaderLanguageSource); >+ mslSourceView.copySources = copyMap; >+ >+ window.localStorage.setItem(LocalStorageMslKeyName, compileResult.metalShaderLanguageSource); >+ } else { >+ mslSourceView.name = "MSL (previous compilation)"; >+ whlslSourceView.errorText = compileResult.error.message; >+ } >+} >+ >+function lexWhitespaceAndComment(src) { >+ // WHLSL permits multiline comments, but the (restricted) MSL lexer doesn't >+ const wsRegex = /^\s+/; >+ const oneLineCommentRegex = /^\/\/(.*?)(\n|$)/; >+ const multilineCommentStartRegex = /^\/\*/; >+ const multilineCommentEndRegex = /\*\//; >+ >+ const tokens = []; >+ >+ while (src.length) { >+ const wsMatch = wsRegex.exec(src); >+ if (wsMatch) { >+ tokens.push({ type: 'whitespace', src: wsMatch[0] }); >+ src = src.substring(wsMatch[0].length); >+ continue; >+ } >+ >+ const oneLineCommentMatch = oneLineCommentRegex.exec(src); >+ if (oneLineCommentMatch) { >+ tokens.push({ type: 'comment', src: oneLineCommentMatch[0] }); >+ src = src.substring(oneLineCommentMatch[0].length); >+ continue; >+ } >+ >+ const multilineCommentStartMatch = multilineCommentStartRegex.exec(src); >+ if (multilineCommentStartMatch) { >+ const multilineCommentEndMatch = multilineCommentEndRegex.exec(src); >+ if (multilineCommentEndMatch) { >+ const index = multilineCommentEndMatch.index + 2; >+ tokens.push({ type: 'comment', src: src.substring(0, index) }); >+ src = src.substring(index); >+ } else { >+ throw new Error(`Multiline comment not terminated properly: '${src}'`); >+ } >+ continue; >+ } >+ >+ throw new Error(`Failed to tokenize whitespace/comment string '${src}'`); >+ } >+ >+ return tokens; >+} >+ >+function lexWHLSL(src) >+{ >+ const lexer = new Lexer("/internal/test", "user", 0, src); >+ let index = 0; >+ let hasTokens = true; >+ let tokens = []; >+ >+ const mapWHLSLTokens = { >+ "floatLiteral": "literal", >+ "intHexLiteral": "literal", >+ "uintHexLiteral": "literal", >+ "intLiteral": "literal", >+ "uintLiteral": "literal" >+ }; >+ const identifiersThatAreTypes = new Set([ >+ "int", "uint", "float", "int32", "uint32", "uint8", "float2", "float3", "float4", "vertex", "fragment", "void", "bool" >+ ]); >+ >+ while (hasTokens) { >+ const next = lexer.next(); >+ if (next) { >+ // tokens.push(next); >+ if (next.index > index) { >+ tokens = tokens.concat(lexWhitespaceAndComment(src.substring(index, next.index))); >+ } >+ index = next.index + next.text.length; >+ let kind = next.kind; >+ if (kind in mapWHLSLTokens) >+ kind = mapWHLSLTokens[kind]; >+ if (kind == "identifier" && identifiersThatAreTypes.has(next.text)) >+ kind = "type"; >+ tokens.push({ type: kind, src: next.text }); >+ } else { >+ hasTokens = false; >+ } >+ } >+ if (index != src.length) { >+ tokens = tokens.concat(lexWhitespaceAndComment(src.substring(index))); >+ } >+ return tokens; >+} >+ >+// Wraps a textarea and a bunch of buttons >+class SourceView { >+ constructor(name, container, lexer, compiler = null, editable = false) >+ { >+ this._name = name; >+ this._container = container; >+ this._lexingDelegate = lexer; >+ this._compiler = compiler; >+ this._copySources = new Map(); >+ this._src = ""; >+ this._tokens = []; >+ this._editable = editable; >+ this._errorText = ""; >+ } >+ >+ get name() { return this._name; } >+ get editable() { return this._editable; } >+ get src() { return this._src; } >+ >+ set src(newSrc) >+ { >+ this._src = newSrc; >+ this._tokens = this._lexingDelegate(this._src); >+ this._updateView(); >+ } >+ >+ get copySources() { return this._copySources; } >+ set copySources(cs) >+ { >+ this._copySources = cs; >+ this._updateView(); >+ } >+ >+ get errorText() { return this._errorText; } >+ >+ set errorText(newErrorText) >+ { >+ this._errorText = newErrorText; >+ if (this._errorBox && newErrorText) >+ this._errorBox.innerText = this._errorText; >+ } >+ >+ set name(newName) >+ { >+ this._name = newName; >+ if (this._header) >+ this._header.innerText = this.name; >+ } >+ >+ _updateView() >+ { >+ this._presentSource(this._tokens, this._container); >+ } >+ >+ _removeAllChildren(node) >+ { >+ while (node.hasChildNodes()) { >+ node.removeChild(node.lastChild); >+ } >+ } >+ >+ _splitTokensIntoLines(tokens) >+ { >+ const lines = []; >+ let currentLine = []; >+ lines.push(currentLine); >+ >+ for (let tok of tokens) { >+ let tokSections = tok.src.split('\n'); >+ for (let i = 0; i < tokSections.length; i++) { >+ let tokSection = tokSections[i]; >+ >+ let span = document.createElement('span'); >+ span.innerText = tokSection; >+ span.className = `code-${tok.type}`; >+ currentLine.push(span); >+ >+ if (i != tokSections.length - 1) { >+ currentLine = []; >+ lines.push(currentLine); >+ } >+ } >+ } >+ >+ return lines; >+ } >+ >+ _presentSource(tokens, container) >+ { >+ this._removeAllChildren(container); >+ >+ this._header = document.createElement('h2'); >+ this._header.innerText = this.name; >+ container.appendChild(this._header); >+ >+ const lines = this._splitTokensIntoLines(tokens); >+ >+ const srcTable = document.createElement('table'); >+ srcTable.className = 'code-source'; >+ >+ for (let i = 0; i < lines.length; i++) { >+ const currentRow = document.createElement('tr'); >+ >+ const currentLineNumber = document.createElement('td'); >+ currentLineNumber.className = 'line-no'; >+ currentLineNumber.dataset.content = (i + 1).toString(); // line-no shown with CSS >+ currentRow.appendChild(currentLineNumber); >+ >+ const currentLineElement = document.createElement('td'); >+ currentLineElement.className = 'line'; >+ for (let span of lines[i]) { >+ currentLineElement.appendChild(span); >+ } >+ currentRow.appendChild(currentLineElement); >+ >+ srcTable.appendChild(currentRow); >+ } >+ >+ container.appendChild(srcTable); >+ >+ if (this.editable) { >+ const editButton = document.createElement('input'); >+ editButton.type = "button"; >+ editButton.value = "Edit"; >+ editButton.addEventListener("click", () => this.edit()); >+ container.appendChild(editButton); >+ } >+ >+ for (let [name, source] of this.copySources) { >+ const copyButton = document.createElement('input'); >+ copyButton.type = "button"; >+ copyButton.value = `Copy ${name}`; >+ copyButton.addEventListener("click", () => { >+ const fakeTextarea = document.createElement('textarea'); >+ fakeTextarea.value = source; >+ document.body.appendChild(fakeTextarea); >+ fakeTextarea.focus(); >+ fakeTextarea.select(); >+ document.execCommand("copy"); >+ document.body.removeChild(fakeTextarea); >+ }); >+ container.appendChild(copyButton); >+ } >+ >+ this._createErrorBox(); >+ } >+ >+ edit() >+ { >+ this._removeAllChildren(this._container); >+ >+ this._header = document.createElement('h2'); >+ this._header.innerText = this.name; >+ this._container.appendChild(this._header); >+ >+ this._textarea = document.createElement('textarea'); >+ this._textarea.value = this.src; >+ this._container.appendChild(this._textarea); >+ >+ const compileButton = document.createElement('input'); >+ compileButton.type = "button"; >+ compileButton.value = "Compile"; >+ compileButton.addEventListener("click", () => this.compile()); >+ this._container.appendChild(compileButton); >+ >+ this._createErrorBox(); >+ } >+ >+ _createErrorBox() >+ { >+ this._errorBox = document.createElement('div'); >+ if (this.errorText) { >+ this._errorBox.innerText = this.errorText; >+ this._errorText = null; >+ } >+ this._container.appendChild(this._errorBox); >+ } >+ >+ compile() >+ { >+ let src = this._textarea ? this._textarea.value : this._src; >+ this._compiler(src); >+ this.src = src; >+ } >+} >+</script> >+</head> >+<body> >+<table cellspacing="0" cellpadding="0"> >+ <tr> >+ <td class="container-cell"> >+ <div id="WHLSLInput"></div> >+ </td> >+ <td class="container-cell"> >+ <div id="MetalOutput"></div> >+ </td> >+ </tr> >+</table> >+</body> >+</html> >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/metal.css b/Tools/WebGPUShadingLanguageRI/Metal/metal.css >new file mode 100644 >index 0000000000000000000000000000000000000000..d315d02232c6244a8c1597461ee88c00db6d2c55 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/metal.css >@@ -0,0 +1,113 @@ >+textarea, .code-source { >+ font-family: "SF Mono", monospace; >+ font-size: 11pt; >+ padding: 5px; >+ width: 800px; >+ height: 800px; >+ margin: 1em 0; >+ display: block; >+} >+ >+input[type=button] { >+ font-size: 16pt; >+} >+ >+.code-source { >+ overflow-x: scroll; >+ overflow-y: scroll; >+ border: 1px solid black; >+ display: block; >+ background-color: #1F1F24; >+ color: white; >+ white-space: nowrap; >+} >+ >+td { >+ border: 1px solid black; >+} >+ >+body { >+ font-family: -apple-system, sans-serif; >+} >+ >+h1, h2 { >+ text-align: center; >+} >+ >+table { >+ width:100%; >+ border:none; >+ border-collapse: collapse; >+ >+} >+ >+.container-cell { >+ width:50%; >+} >+ >+th, td { >+ border:none; >+ vertical-align:top; >+ margin: 0; >+ -webkit-border-horizontal-spacing: 0px; >+ -webkit-border-vertical-spacing: 0px; >+ border-collapse: collapse; >+ box-sizing: border-box; >+ border-spacing: 0; >+} >+ >+.code-comment { >+ color: #9e9e9e; >+ font-style: italic; >+ white-space: pre; >+} >+ >+.code-comment-note { >+ color: #F4E218; >+ font-style: italic; >+ font-weight:bold; >+} >+ >+.code-keyword, .code-type { >+ color: #FD5FA2; >+ font-weight: bold; >+} >+ >+.code-literal { >+ color: #9686F5; >+} >+ >+.code-attribute { >+ color: #AEF29D; >+ font-weight: bold; >+} >+ >+.code-whitespace { >+ white-space: pre; >+} >+ >+.code-error { >+ color: #fc5a3a; >+ text-decoration: underline; >+} >+ >+.code-directive { >+ color: #FC8F3F; >+} >+ >+.line-no { >+ color: #616065; >+ text-align: right; >+ -webkit-user-select:none; >+ padding-left: 5px; >+ padding-right: 5px; >+ border-right: 1px solid #616065; >+} >+ >+.line-no::before { >+ content: attr(data-content); >+} >+ >+.line { >+ padding-left: 5px; >+} >\ No newline at end of file >diff --git a/Tools/WebGPUShadingLanguageRI/Metal/tsconfig.json b/Tools/WebGPUShadingLanguageRI/Metal/tsconfig.json >new file mode 100644 >index 0000000000000000000000000000000000000000..04f370c3827a319a7c27e1803f0e4740ed7fcd88 >--- /dev/null >+++ b/Tools/WebGPUShadingLanguageRI/Metal/tsconfig.json >@@ -0,0 +1,26 @@ >+{ >+ "version": "2.0.0", >+ "tasks": [ >+ { >+ "type": "typescript", >+ "tsconfig": "tsconfig.json", >+ "problemMatcher": [ >+ "$tsc" >+ ], >+ "group": { >+ "kind": "build", >+ "isDefault": true >+ } >+ } >+ ], >+ "compilerOptions": { >+ "target": "es6", >+ "module": "amd", >+ "sourceMap": true, >+ "lib": [ >+ "es6" >+ ], >+ "types": [], >+ "outFile": "compiler.js" >+ } >+} >diff --git a/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js b/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js >index a0181468a25ed4c09050d7bf1d61f44ed3a0aa66..24a209433bde0c1bbc2085654a0612c8c65fe9cd 100644 >--- a/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js >+++ b/Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js >@@ -42,6 +42,13 @@ function synthesizeStructAccessors(program) > nativeFunc.implementation = (argumentList, node) => { > return implementation(argumentList, field.offset, type.size, field.type.size); > }; >+ >+ nativeFunc.implementationData = { >+ name: field.name, >+ type: type, >+ offset: field.offset, >+ size: field.type.size >+ }; > } > > function createFieldType()
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