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