[WHSL] Implement semantics
authormmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 25 Sep 2018 21:21:10 +0000 (21:21 +0000)
committermmaxfield@apple.com <mmaxfield@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 25 Sep 2018 21:21:10 +0000 (21:21 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189134

Reviewed by Filip Pizlo.

This patch adds parsing support and adds the checks required to support HLSL-style semantics.

There are 3 places where semantics are allowed:

1. In a field in a struct. E.g.

struct R {
    float4 position : SV_Position;
}
vertex R foo() {
    ...
}

2. In a parameter of a function. If the function is not an entry point, the semantic is ignored.

compute void foo(device float[] data : buffer(u0)) {
    ...
}

3. On a function. This is so you don't have to create structs for the return types of simple vertex shaders.

vertex float4 foo() : SV_Position {
    ...
}

The semantics are partitioned into 4 types:

1. Built-in variables. For example, : SV_Position.
2. Resources. For example, : register(u0).
3. Stage-in / stage-out variables. For example, : attribute(0).
4. Specialization constants. For example, : specialized.

The semantics are validated according to a collection of rules:

- The same entry point can't list the same semantic twice
- Each built-in type has an appropriate type that is listed in the spec.
- Each built-in is appropriate as either an input or an output of a particular shader stage
- Resource semantics have to have the appropriate type (e.g. can't use register(s0) on a buffer)
- Resources can't be output from entry points
- The resource semantic mode must match the address space of the resource
- Stage-in and stage-out variables have to be POD
- Specialization constants can't be output from entry points, and have to be numbers

* WebGPUShadingLanguageRI/All.js:
* WebGPUShadingLanguageRI/BuiltInSemantic.js: Added.
(BuiltInSemantic):
(BuiltInSemantic.prototype.get name):
(BuiltInSemantic.prototype.get extraArguments):
(BuiltInSemantic.prototype.isAcceptableType):
(BuiltInSemantic.prototype.isAcceptableForShaderType):
(BuiltInSemantic.prototype.toString):
* WebGPUShadingLanguageRI/CallExpression.js:
(CallExpression.prototype._resolveWithOperatorAnderIndexer):
(CallExpression.prototype._resolveWithOperatorLength):
* WebGPUShadingLanguageRI/Checker.js:
(Checker):
(Checker.prototype.visitProgram):
(Checker.prototype._checkSemantics.Item):
(Checker.prototype._checkSemantics.Item.prototype.get type):
(Checker.prototype._checkSemantics.Item.prototype.get semantic):
(Checker.prototype._checkSemantics.Gatherer):
(Checker.prototype._checkSemantics.Gatherer.prototype.reset):
(Checker.prototype._checkSemantics.Gatherer.prototype.set currentSemantic):
(Checker.prototype._checkSemantics.Gatherer.prototype.get currentSemantic):
(Checker.prototype._checkSemantics.Gatherer.prototype.get result):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitEnumType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitVectorType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitMatrixType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitNativeType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitStructType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitTypeRef):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitPtrType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitArrayRefType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitArrayType):
(Checker.prototype._checkSemantics.Gatherer.prototype.visitFuncParameter):
(Checker.prototype._checkSemantics.checkDuplicateSemantics):
(Checker.prototype._checkSemantics.checkSemanticTypes):
(Checker.prototype._checkSemantics.checkSemanticForShaderType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitEnumType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitArrayType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitVectorType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitMatrixType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitNativeType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitPtrType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitArrayRefType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitStructType):
(Checker.prototype._checkSemantics.PODChecker.prototype.visitTypeRef):
(Checker.prototype._checkSemantics.PODChecker):
(Checker.prototype._checkSemantics.checkPODData):
(Checker.prototype._checkSemantics):
(Checker.prototype._checkShaderType):
(Checker.prototype._checkOperatorOverload):
(Checker.prototype.visitFuncDef):
(Checker.prototype.visitEnumType):
(Checker.prototype.visitArrayType):
(Checker.prototype.visitMakePtrExpression):
(Checker.prototype.visitMakeArrayRefExpression):
(Checker.prototype._finishVisitingPropertyAccess):
(Checker.prototype.visitIndexExpression):
(Checker.prototype.visitReturn):
(Checker.prototype.visitSwitchStatement):
(Checker.prototype.visitTernaryExpression):
(Checker.prototype.visitCallExpression):
* WebGPUShadingLanguageRI/Field.js:
(Field):
(Field.prototype.get semantic):
(Field.prototype.toString):
* WebGPUShadingLanguageRI/Func.js:
(Func):
(Func.prototype.get semantic):
(Func.prototype.toDeclString):
* WebGPUShadingLanguageRI/FuncDef.js:
(FuncDef):
* WebGPUShadingLanguageRI/FuncParameter.js:
(FuncParameter):
(FuncParameter.prototype.get semantic):
(FuncParameter.prototype.toString):
* WebGPUShadingLanguageRI/Intrinsics.js:
* WebGPUShadingLanguageRI/LateChecker.js:
(LateChecker.prototype.visitReferenceType):
(LateChecker):
(LateChecker.prototype._checkShaderType): Deleted.
(LateChecker.prototype.visitFuncDef): Deleted.
* WebGPUShadingLanguageRI/NativeFunc.js:
(NativeFunc):
* WebGPUShadingLanguageRI/Parse.js:
(parseParameter):
(parseFuncDecl):
(parseFuncDef):
(parseStageInOutSemantic):
(parseResourceSemantic):
(parseSpecializationConstantSemantic):
(parseBuiltInSemantic):
(parseField):
(parseNativeFunc):
* WebGPUShadingLanguageRI/ProgramWithUnnecessaryThingsRemoved.js:
(programWithUnnecessaryThingsRemoved):
* WebGPUShadingLanguageRI/ResourceSemantic.js: Added.
(ResourceSemantic):
(ResourceSemantic.prototype.get resourceMode):
(ResourceSemantic.prototype.get index):
(ResourceSemantic.prototype.get space):
(ResourceSemantic.prototype.isAcceptableType):
(ResourceSemantic.prototype.isAcceptableForShaderType):
(ResourceSemantic.prototype.toString):
* WebGPUShadingLanguageRI/Rewriter.js:
(Rewriter.prototype.visitFuncParameter):
(Rewriter.prototype.visitField):
(Rewriter.prototype.visitBuiltInSemantic):
(Rewriter.prototype.visitResourceSemantic):
(Rewriter.prototype.visitStageInOutSemantic):
(Rewriter.prototype.visitSpecializationConstantSemantic):
(Rewriter):
* WebGPUShadingLanguageRI/SPIRV.html:
* WebGPUShadingLanguageRI/Semantic.js: Added.
(Semantic):
(Semantic.prototype.get origin):
(Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitBuiltInSemantic):
(Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitResourceSemantic):
(Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitStageInOutSemantic):
(Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitSpecializationConstantSemantic):
(Semantic.prototype.equalToOtherSemantic.Comparer):
(Semantic.prototype.equalToOtherSemantic):
* WebGPUShadingLanguageRI/SpecializationConstantSemantic.js: Copied from Tools/WebGPUShadingLanguageRI/Field.js.
(SpecializationConstantSemantic):
(SpecializationConstantSemantic.prototype.isAcceptableType):
(SpecializationConstantSemantic.prototype.isAcceptableForShaderType):
(SpecializationConstantSemantic.prototype.toString):
* WebGPUShadingLanguageRI/StageInOutSemantic.js: Copied from Tools/WebGPUShadingLanguageRI/FuncDef.js.
(StageInOutSemantic):
(StageInOutSemantic.prototype.get index):
(StageInOutSemantic.prototype.isAcceptableType):
(StageInOutSemantic.prototype.isAcceptableForShaderType):
(StageInOutSemantic.prototype.toString):
* WebGPUShadingLanguageRI/StatementCloner.js:
(StatementCloner.prototype.visitNativeFunc):
* WebGPUShadingLanguageRI/SynthesizeArrayOperatorLength.js:
(synthesizeArrayOperatorLength):
* WebGPUShadingLanguageRI/SynthesizeCopyConstructorOperator.js:
(synthesizeCopyConstructorOperator):
* WebGPUShadingLanguageRI/SynthesizeDefaultConstructorOperator.js:
(synthesizeDefaultConstructorOperator):
* WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js:
(synthesizeEnumFunctions):
* WebGPUShadingLanguageRI/SynthesizeStructAccessors.js:
(setupAnder):
(synthesizeStructAccessorsForStructType):
* WebGPUShadingLanguageRI/Test.html:
* WebGPUShadingLanguageRI/Test.js:
(tests.shaderTypes):
* WebGPUShadingLanguageRI/Visitor.js:
(Visitor.prototype.visitFunc):
(Visitor.prototype.visitFuncParameter):
(Visitor.prototype.visitField):
(Visitor.prototype.visitBuiltInSemantic):
(Visitor.prototype.visitResourceSemantic):
(Visitor.prototype.visitStageInOutSemantic):
(Visitor.prototype.visitSpecializationConstantSemantic):
(Visitor):
* WebGPUShadingLanguageRI/index.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@236475 268f45cc-cd09-0410-ab3c-d52691b4dbfc

30 files changed:
Tools/ChangeLog
Tools/WebGPUShadingLanguageRI/All.js
Tools/WebGPUShadingLanguageRI/BuiltInSemantic.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/CallExpression.js
Tools/WebGPUShadingLanguageRI/Checker.js
Tools/WebGPUShadingLanguageRI/Field.js
Tools/WebGPUShadingLanguageRI/Func.js
Tools/WebGPUShadingLanguageRI/FuncDef.js
Tools/WebGPUShadingLanguageRI/FuncParameter.js
Tools/WebGPUShadingLanguageRI/Intrinsics.js
Tools/WebGPUShadingLanguageRI/LateChecker.js
Tools/WebGPUShadingLanguageRI/NativeFunc.js
Tools/WebGPUShadingLanguageRI/Parse.js
Tools/WebGPUShadingLanguageRI/ProgramWithUnnecessaryThingsRemoved.js
Tools/WebGPUShadingLanguageRI/ResourceSemantic.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/Rewriter.js
Tools/WebGPUShadingLanguageRI/SPIRV.html
Tools/WebGPUShadingLanguageRI/Semantic.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/SpecializationConstantSemantic.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/StageInOutSemantic.js [new file with mode: 0644]
Tools/WebGPUShadingLanguageRI/StatementCloner.js
Tools/WebGPUShadingLanguageRI/SynthesizeArrayOperatorLength.js
Tools/WebGPUShadingLanguageRI/SynthesizeCopyConstructorOperator.js
Tools/WebGPUShadingLanguageRI/SynthesizeDefaultConstructorOperator.js
Tools/WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js
Tools/WebGPUShadingLanguageRI/SynthesizeStructAccessors.js
Tools/WebGPUShadingLanguageRI/Test.html
Tools/WebGPUShadingLanguageRI/Test.js
Tools/WebGPUShadingLanguageRI/Visitor.js
Tools/WebGPUShadingLanguageRI/index.html

index 81be6b2..d0dccff 100644 (file)
@@ -1,3 +1,211 @@
+2018-09-25  Myles C. Maxfield  <mmaxfield@apple.com>
+
+        [WHSL] Implement semantics
+        https://bugs.webkit.org/show_bug.cgi?id=189134
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds parsing support and adds the checks required to support HLSL-style semantics.
+
+        There are 3 places where semantics are allowed:
+
+        1. In a field in a struct. E.g.
+
+        struct R {
+            float4 position : SV_Position;
+        }
+        vertex R foo() {
+            ...
+        }
+
+        2. In a parameter of a function. If the function is not an entry point, the semantic is ignored.
+
+        compute void foo(device float[] data : buffer(u0)) {
+            ...
+        }
+
+        3. On a function. This is so you don't have to create structs for the return types of simple vertex shaders.
+
+        vertex float4 foo() : SV_Position {
+            ...
+        }
+
+        The semantics are partitioned into 4 types:
+
+        1. Built-in variables. For example, : SV_Position.
+        2. Resources. For example, : register(u0).
+        3. Stage-in / stage-out variables. For example, : attribute(0).
+        4. Specialization constants. For example, : specialized.
+
+        The semantics are validated according to a collection of rules:
+
+        - The same entry point can't list the same semantic twice
+        - Each built-in type has an appropriate type that is listed in the spec. 
+        - Each built-in is appropriate as either an input or an output of a particular shader stage
+        - Resource semantics have to have the appropriate type (e.g. can't use register(s0) on a buffer)
+        - Resources can't be output from entry points
+        - The resource semantic mode must match the address space of the resource
+        - Stage-in and stage-out variables have to be POD
+        - Specialization constants can't be output from entry points, and have to be numbers
+
+        * WebGPUShadingLanguageRI/All.js:
+        * WebGPUShadingLanguageRI/BuiltInSemantic.js: Added.
+        (BuiltInSemantic):
+        (BuiltInSemantic.prototype.get name):
+        (BuiltInSemantic.prototype.get extraArguments):
+        (BuiltInSemantic.prototype.isAcceptableType):
+        (BuiltInSemantic.prototype.isAcceptableForShaderType):
+        (BuiltInSemantic.prototype.toString):
+        * WebGPUShadingLanguageRI/CallExpression.js:
+        (CallExpression.prototype._resolveWithOperatorAnderIndexer):
+        (CallExpression.prototype._resolveWithOperatorLength):
+        * WebGPUShadingLanguageRI/Checker.js:
+        (Checker):
+        (Checker.prototype.visitProgram):
+        (Checker.prototype._checkSemantics.Item):
+        (Checker.prototype._checkSemantics.Item.prototype.get type):
+        (Checker.prototype._checkSemantics.Item.prototype.get semantic):
+        (Checker.prototype._checkSemantics.Gatherer):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.reset):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.set currentSemantic):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.get currentSemantic):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.get result):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitEnumType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitVectorType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitMatrixType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitNativeType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitStructType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitTypeRef):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitPtrType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitArrayRefType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitArrayType):
+        (Checker.prototype._checkSemantics.Gatherer.prototype.visitFuncParameter):
+        (Checker.prototype._checkSemantics.checkDuplicateSemantics):
+        (Checker.prototype._checkSemantics.checkSemanticTypes):
+        (Checker.prototype._checkSemantics.checkSemanticForShaderType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitEnumType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitArrayType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitVectorType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitMatrixType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitNativeType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitPtrType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitArrayRefType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitStructType):
+        (Checker.prototype._checkSemantics.PODChecker.prototype.visitTypeRef):
+        (Checker.prototype._checkSemantics.PODChecker):
+        (Checker.prototype._checkSemantics.checkPODData):
+        (Checker.prototype._checkSemantics):
+        (Checker.prototype._checkShaderType):
+        (Checker.prototype._checkOperatorOverload):
+        (Checker.prototype.visitFuncDef):
+        (Checker.prototype.visitEnumType):
+        (Checker.prototype.visitArrayType):
+        (Checker.prototype.visitMakePtrExpression):
+        (Checker.prototype.visitMakeArrayRefExpression):
+        (Checker.prototype._finishVisitingPropertyAccess):
+        (Checker.prototype.visitIndexExpression):
+        (Checker.prototype.visitReturn):
+        (Checker.prototype.visitSwitchStatement):
+        (Checker.prototype.visitTernaryExpression):
+        (Checker.prototype.visitCallExpression):
+        * WebGPUShadingLanguageRI/Field.js:
+        (Field):
+        (Field.prototype.get semantic):
+        (Field.prototype.toString):
+        * WebGPUShadingLanguageRI/Func.js:
+        (Func):
+        (Func.prototype.get semantic):
+        (Func.prototype.toDeclString):
+        * WebGPUShadingLanguageRI/FuncDef.js:
+        (FuncDef):
+        * WebGPUShadingLanguageRI/FuncParameter.js:
+        (FuncParameter):
+        (FuncParameter.prototype.get semantic):
+        (FuncParameter.prototype.toString):
+        * WebGPUShadingLanguageRI/Intrinsics.js:
+        * WebGPUShadingLanguageRI/LateChecker.js:
+        (LateChecker.prototype.visitReferenceType):
+        (LateChecker):
+        (LateChecker.prototype._checkShaderType): Deleted.
+        (LateChecker.prototype.visitFuncDef): Deleted.
+        * WebGPUShadingLanguageRI/NativeFunc.js:
+        (NativeFunc):
+        * WebGPUShadingLanguageRI/Parse.js:
+        (parseParameter):
+        (parseFuncDecl):
+        (parseFuncDef):
+        (parseStageInOutSemantic):
+        (parseResourceSemantic):
+        (parseSpecializationConstantSemantic):
+        (parseBuiltInSemantic):
+        (parseField):
+        (parseNativeFunc):
+        * WebGPUShadingLanguageRI/ProgramWithUnnecessaryThingsRemoved.js:
+        (programWithUnnecessaryThingsRemoved):
+        * WebGPUShadingLanguageRI/ResourceSemantic.js: Added.
+        (ResourceSemantic):
+        (ResourceSemantic.prototype.get resourceMode):
+        (ResourceSemantic.prototype.get index):
+        (ResourceSemantic.prototype.get space):
+        (ResourceSemantic.prototype.isAcceptableType):
+        (ResourceSemantic.prototype.isAcceptableForShaderType):
+        (ResourceSemantic.prototype.toString):
+        * WebGPUShadingLanguageRI/Rewriter.js:
+        (Rewriter.prototype.visitFuncParameter):
+        (Rewriter.prototype.visitField):
+        (Rewriter.prototype.visitBuiltInSemantic):
+        (Rewriter.prototype.visitResourceSemantic):
+        (Rewriter.prototype.visitStageInOutSemantic):
+        (Rewriter.prototype.visitSpecializationConstantSemantic):
+        (Rewriter):
+        * WebGPUShadingLanguageRI/SPIRV.html:
+        * WebGPUShadingLanguageRI/Semantic.js: Added.
+        (Semantic):
+        (Semantic.prototype.get origin):
+        (Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitBuiltInSemantic):
+        (Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitResourceSemantic):
+        (Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitStageInOutSemantic):
+        (Semantic.prototype.equalToOtherSemantic.Comparer.prototype.visitSpecializationConstantSemantic):
+        (Semantic.prototype.equalToOtherSemantic.Comparer):
+        (Semantic.prototype.equalToOtherSemantic):
+        * WebGPUShadingLanguageRI/SpecializationConstantSemantic.js: Copied from Tools/WebGPUShadingLanguageRI/Field.js.
+        (SpecializationConstantSemantic):
+        (SpecializationConstantSemantic.prototype.isAcceptableType):
+        (SpecializationConstantSemantic.prototype.isAcceptableForShaderType):
+        (SpecializationConstantSemantic.prototype.toString):
+        * WebGPUShadingLanguageRI/StageInOutSemantic.js: Copied from Tools/WebGPUShadingLanguageRI/FuncDef.js.
+        (StageInOutSemantic):
+        (StageInOutSemantic.prototype.get index):
+        (StageInOutSemantic.prototype.isAcceptableType):
+        (StageInOutSemantic.prototype.isAcceptableForShaderType):
+        (StageInOutSemantic.prototype.toString):
+        * WebGPUShadingLanguageRI/StatementCloner.js:
+        (StatementCloner.prototype.visitNativeFunc):
+        * WebGPUShadingLanguageRI/SynthesizeArrayOperatorLength.js:
+        (synthesizeArrayOperatorLength):
+        * WebGPUShadingLanguageRI/SynthesizeCopyConstructorOperator.js:
+        (synthesizeCopyConstructorOperator):
+        * WebGPUShadingLanguageRI/SynthesizeDefaultConstructorOperator.js:
+        (synthesizeDefaultConstructorOperator):
+        * WebGPUShadingLanguageRI/SynthesizeEnumFunctions.js:
+        (synthesizeEnumFunctions):
+        * WebGPUShadingLanguageRI/SynthesizeStructAccessors.js:
+        (setupAnder):
+        (synthesizeStructAccessorsForStructType):
+        * WebGPUShadingLanguageRI/Test.html:
+        * WebGPUShadingLanguageRI/Test.js:
+        (tests.shaderTypes):
+        * WebGPUShadingLanguageRI/Visitor.js:
+        (Visitor.prototype.visitFunc):
+        (Visitor.prototype.visitFuncParameter):
+        (Visitor.prototype.visitField):
+        (Visitor.prototype.visitBuiltInSemantic):
+        (Visitor.prototype.visitResourceSemantic):
+        (Visitor.prototype.visitStageInOutSemantic):
+        (Visitor.prototype.visitSpecializationConstantSemantic):
+        (Visitor):
+        * WebGPUShadingLanguageRI/index.html:
+
 2018-09-25  Thomas Denney  <tdenney@apple.com>
 
         [WHLSL] Test suite for Metal code generation
index 9046ed4..7039252 100644 (file)
@@ -35,6 +35,7 @@ load("CreateLiteral.js");
 load("CreateLiteralType.js");
 load("PropertyAccessExpression.js");
 load("NativeType.js");
+load("Semantic.js");
 
 load("AddressSpace.js");
 load("AllocateAtEntryPoints.js");
@@ -46,6 +47,7 @@ load("AutoWrapper.js");
 load("Block.js");
 load("BoolLiteral.js");
 load("Break.js");
+load("BuiltInSemantic.js");
 load("BuiltinMatrixGetter.js");
 load("BuiltinMatrixSetter.js");
 load("BuiltinVectorGetter.js");
@@ -141,10 +143,13 @@ load("ResolveNames.js");
 load("ResolveOverloadImpl.js");
 load("ResolveProperties.js");
 load("ResolveTypeDefs.js");
+load("ResourceSemantic.js");
 load("Return.js");
 load("ReturnChecker.js");
 load("ReturnException.js");
 load("Sampler.js");
+load("SpecializationConstantSemantic.js");
+load("StageInOutSemantic.js");
 load("StandardLibrary.js");
 load("StatementCloner.js");
 load("StructLayoutBuilder.js");
diff --git a/Tools/WebGPUShadingLanguageRI/BuiltInSemantic.js b/Tools/WebGPUShadingLanguageRI/BuiltInSemantic.js
new file mode 100644 (file)
index 0000000..2539332
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * 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";
+
+class BuiltInSemantic extends Semantic {
+    constructor(origin, name, ...extraArguments)
+    {
+        super(origin);
+        this._name = name;
+        this._extraArguments = extraArguments;
+    }
+
+    get name() { return this._name; }
+    get extraArguments() { return this._extraArguments; }
+
+    isAcceptableType(type, program)
+    {
+        let requiredType;
+        switch (this.name) {
+        case "SV_InstanceID":
+            requiredType = program.intrinsics.uint;
+            break;
+        case "SV_VertexID":
+            requiredType = program.intrinsics.uint;
+            break;
+        case "PSIZE":
+            requiredType = program.intrinsics.float;
+            break;
+        case "SV_Position":
+            requiredType = program.intrinsics["vector<float, 4>"];
+            break;
+        case "SV_IsFrontFace":
+            requiredType = program.intrinsics.bool;
+            break;
+        case "SV_SampleIndex":
+            requiredType = program.intrinsics.uint;
+            break;
+        case "SV_InnerCoverage":
+            requiredType = program.intrinsics.uint;
+            break;
+        case "SV_Target":
+            requiredType = program.intrinsics["vector<float, 4>"];
+            break;
+        case "SV_Depth":
+            requiredType = program.intrinsics.float;
+            break;
+        case "SV_Coverage":
+            requiredType = program.intrinsics.uint;
+            break;
+        case "SV_DispatchThreadID":
+            requiredType = program.intrinsics["vector<uint, 3>"];
+            break;
+        case "SV_GroupID":
+            requiredType = program.intrinsics["vector<uint, 3>"];
+            break;
+        case "SV_GroupIndex":
+            requiredType = program.intrinsics.uint;
+            break;
+        case "SV_GroupThreadID":
+            requiredType = program.intrinsics["vector<uint, 3>"];
+            break;
+        default:
+            throw new Error(`Unknown semantic: ${this.name}`);
+        }
+
+        return type.equals(requiredType);
+    }
+
+    isAcceptableForShaderType(direction, shaderType)
+    {
+        switch (shaderType) {
+        case "vertex":
+            switch (direction) {
+            case "input":
+                switch (this.name) {
+                case "SV_InstanceID":
+                case "SV_VertexID":
+                    return true;
+                default:
+                    return false;
+                }
+            case "output":
+                switch (this.name) {
+                case "PSIZE":
+                case "SV_Position":
+                    return true;
+                default:
+                    return false;
+                }
+            }
+        case "fragment":
+            switch (direction) {
+            case "input":
+                switch (this.name) {
+                case "SV_IsFrontFace":
+                case "SV_SampleIndex":
+                case "SV_InnerCoverage":
+                    return true;
+                default:
+                    return false;
+                }
+            case "output":
+                switch (this.name) {
+                case "SV_Target":
+                case "SV_Depth":
+                case "SV_Coverage":
+                    return true;
+                default:
+                    return false;
+                }
+            }
+        case "compute":
+            if (direction != "input")
+                return false;
+            switch (this.name) {
+            case "SV_DispatchThreadID":
+            case "SV_GroupID":
+            case "SV_GroupIndex":
+            case "SV_GroupThreadID":
+                return true;
+            default:
+                return false;
+            }
+        case "test":
+            return true;
+        default:
+            throw new Error(`Unknown shade type: ${shaderType}`);
+        }
+    }
+
+    toString()
+    {
+        let result = this.name;
+        if (this.extraArguments)
+            result += this.extraArguments.join("");
+        return result;
+    }
+}
+
index 4e4d65f..b4c0da7 100644 (file)
@@ -129,7 +129,7 @@ class CallExpression extends Expression {
         const func = new NativeFunc(this.origin, "operator&[]", this.resultType, [
             new FuncParameter(this.origin, null, arrayRefType),
             new FuncParameter(this.origin, null, uintType)
-        ], false, null);
+        ], false);
 
         arrayRefAccessor.instantiateImplementation(func);
 
@@ -144,7 +144,7 @@ class CallExpression extends Expression {
             const arrayType = this.argumentTypes[0];
             const func = new NativeFunc(this.origin, "operator.length", this.resultType, [
                 new FuncParameter(this.origin, null, arrayType)
-            ], false, null);
+            ], false);
             func.implementation = (args, node) => EPtr.box(arrayType.numElementsValue);
             return func;
         } else if (this.argumentTypes[0].isArrayRef) {
@@ -153,7 +153,7 @@ class CallExpression extends Expression {
             const operatorLength = new OperatorArrayRefLength(arrayRefType.toString(), addressSpace);
             const func = new NativeFunc(this.origin, "operator.length", this.resultType, [
                 new FuncParameter(this.origin, null, arrayRefType)
-            ], false, null);
+            ], false);
             operatorLength.instantiateImplementation(func);
             return func;
         } else
index 14f9d2b..8655cf3 100644 (file)
@@ -32,15 +32,16 @@ class Checker extends Visitor {
         this._currentStatement = null;
         this._vertexEntryPoints = new Set();
         this._fragmentEntryPoints = new Set();
+        this._computeEntryPoints = new Set();
     }
-    
+
     visitProgram(node)
     {
         let doStatement = statement => {
             this._currentStatement = statement;
             statement.visit(this);
         }
-        
+
         for (let type of node.types.values()) {
             if (type instanceof Array) {
                 for (let constituentType of type)
@@ -59,10 +60,201 @@ class Checker extends Visitor {
         }
     }
 
+    _checkSemantics(node)
+    {
+        class Item {
+            constructor(type, semantic)
+            {
+                if (node.shaderType != "test" && !semantic)
+                    throw new WTypeError(node.origin.originString, "An entry-point input/output exists which doesn't have a semantic");
+                this._type = type;
+                this._semantic = semantic;
+            }
+
+            get type() { return this._type; }
+            get semantic() { return this._semantic; }
+        }
+
+        let program = this._program;
+        class Gatherer extends Visitor {
+            constructor(currentSemantic = null)
+            {
+                super();
+                this._currentSemantic = currentSemantic;
+                this._result = [];
+            }
+
+            reset(currentSemantic = null)
+            {
+                this.currentSemantic = currentSemantic;
+            }
+
+            set currentSemantic(value) { this._currentSemantic = value; }
+            get currentSemantic() { return this._currentSemantic; }
+            get result() { return this._result; }
+
+            visitEnumType(node)
+            {
+                this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitVectorType(node)
+            {
+                this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitMatrixType(node)
+            {
+                this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitNativeType(node)
+            {
+                if (program.intrinsics.void.equals(node)) {
+                    if (this.currentSemantic)
+                        throw new WTypeError(node.origin.originString, "Void can't have a semantic");
+                } else
+                    this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitStructType(node)
+            {
+                if (this.currentSemantic != null)
+                    throw new WTypeError(node.origin.originString, "Structs inside entry point signatures can't have semantics.");
+                for (let field of node.fields) {
+                    this.currentSemantic = field.semantic;
+                    field.type.visit(this);
+                }
+            }
+
+            visitTypeRef(node)
+            {
+                node.type.visit(this);
+            }
+
+            visitPtrType(node)
+            {
+                this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitArrayRefType(node)
+            {
+                this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitArrayType(node)
+            {
+                this.result.push(new Item(node, this.currentSemantic));
+            }
+
+            visitFuncParameter(node)
+            {
+                this._currentSemantic = node.semantic;
+                node.type.visit(this);
+            }
+        }
+
+        let inputGatherer = new Gatherer();
+        for (let parameter of node.parameters) {
+            inputGatherer.reset();
+            parameter.visit(inputGatherer);
+        }
+        let outputGatherer = new Gatherer(node.semantic);
+        node.returnType.visit(outputGatherer);
+
+        function checkDuplicateSemantics(items) {
+            // FIXME: Make this faster than O(n^2)
+            for (let i = 0; i < items.length; ++i) {
+                for (let j = i + 1; j < items.length; ++j) {
+                    if (items[i].semantic && items[i].semantic.equalToOtherSemantic(items[j].semantic))
+                        throw new WTypeError(node.origin.originString, `Duplicate semantic found in entry point: ${items[i].semantic}`);
+                }
+            }
+        }
+        checkDuplicateSemantics(inputGatherer.result);
+        checkDuplicateSemantics(outputGatherer.result);
+
+        function checkSemanticTypes(items) {
+            for (let item of items) {
+                if (item.semantic && !item.semantic.isAcceptableType(item.type, program))
+                    throw new WTypeError(node.origin.originString, `Semantic ${item.semantic} is unnacceptable type ${item.type}`);
+            }
+        }
+        checkSemanticTypes(inputGatherer.result);
+        checkSemanticTypes(outputGatherer.result);
+
+        function checkSemanticForShaderType(items, direction) {
+            for (let item of items) {
+                if (item.semantic && !item.semantic.isAcceptableForShaderType(direction, node.shaderType))
+                    throw new WTypeError(node.origin.originString, `Semantic ${item.semantic} is unnacceptable as an ${direction} of shader type ${node.shaderType}`);
+            }
+        }
+        checkSemanticForShaderType(inputGatherer.result, "input");
+        checkSemanticForShaderType(outputGatherer.result, "output");
+
+        class PODChecker extends Visitor {
+            visitEnumType(node)
+            {
+                return true;
+            }
+
+            visitArrayType(node)
+            {
+                return node.elementType.visit(this);
+            }
+
+            visitVectorType(node)
+            {
+                return true;
+            }
+
+            visitMatrixType(node)
+            {
+                return true;
+            }
+
+            visitNativeType(node)
+            {
+                return node.isNumber;
+            }
+
+            visitPtrType(node)
+            {
+                return false;
+            }
+
+            visitArrayRefType(node)
+            {
+                return false;
+            }
+
+            visitStructType(node)
+            {
+                let result = true;
+                for (let field of node.fields)
+                    result = result && field.visit(this);
+                return result;
+            }
+
+            visitTypeRef(node)
+            {
+                return node.type.visit(this);
+            }
+        }
+        function checkPODData(items) {
+            for (let item of items) {
+                if ((item.type instanceof PtrType) || (item.type instanceof ArrayRefType) || (item.type instanceof ArrayType)) {
+                    if (!item.type.elementType.visit(new PODChecker()))
+                        throw new WTypeError(node.origin.originString, "Buffer attached to entry point needs to only contain POD types");
+                }
+            }
+        }
+        checkPODData(inputGatherer.result);
+        checkPODData(outputGatherer.result);
+    }
+
     _checkShaderType(node)
     {
-        // FIXME: Relax these checks once we have implemented support for textures and samplers.
-        let shaderFunc = node;
         switch (node.shaderType) {
         case "vertex":
             if (this._vertexEntryPoints.has(node.name))
@@ -74,6 +266,15 @@ class Checker extends Visitor {
                 throw new WTypeError(node.origin.originString, "Duplicate fragment entry point name " + node.name);
             this._fragmentEntryPoints.add(node.name);
             break;
+        case "compute":
+            if (this._computeEntryPoints.has(node.name))
+                throw new WTypeError(node.origin.originString, "Duplicate compute entry point name " + node.name);
+            this._computeEntryPoints.add(node.name);
+            break;
+        case "test":
+            break;
+        default:
+            throw new Error("Bad shader type: " + node.shaderType);
         }
     }
 
@@ -84,7 +285,7 @@ class Checker extends Visitor {
 
         if (!func.name.startsWith("operator"))
             throw new Error("Bad operator overload name: " + func.name);
-        
+
         let checkGetter = (kind) => {
             let numExpectedParameters = kind == "index" ? 2 : 1;
             if (func.parameters.length != numExpectedParameters)
@@ -92,7 +293,7 @@ class Checker extends Visitor {
             if (func.parameterTypes[0].unifyNode.isPtr)
                 throw new WTypeError(func.origin.originString, "Cannot have getter for pointer type: " + func.parameterTypes[0]);
         };
-        
+
         let checkSetter = (kind) => {
             let numExpectedParameters = kind == "index" ? 3 : 2;
             if (func.parameters.length != numExpectedParameters)
@@ -114,7 +315,7 @@ class Checker extends Visitor {
             if (!resultType.equals(valueType))
                 throw new WTypeError(func.origin.originString, "Setter and getter must agree on value type (getter at " + overload.func.origin.originString + " says " + resultType + " while this setter says " + valueType + ")");
         };
-        
+
         let checkAnder = (kind) => {
             let numExpectedParameters = kind == "index" ? 2 : 1;
             if (func.parameters.length != numExpectedParameters)
@@ -124,7 +325,7 @@ class Checker extends Visitor {
             if (!func.parameterTypes[0].unifyNode.isRef)
                 throw new WTypeError(func.origin.originString, "Parameter to ander is not a reference: " + func.parameterTypes[0]);
         };
-        
+
         switch (func.name) {
         case "operator cast":
             break;
@@ -189,44 +390,46 @@ class Checker extends Visitor {
             throw new Error("Parser accepted unrecognized operator: " + func.name);
         }
     }
-    
+
     visitFuncDef(node)
     {
-        if (node.shaderType)
+        if (node.shaderType) {
             this._checkShaderType(node);
+            this._checkSemantics(node);
+        }
         this._checkOperatorOverload(node, name => this._program.functions.get(name));
         node.body.visit(this);
     }
-    
+
     visitEnumType(node)
     {
         node.baseType.visit(this);
-        
+
         let baseType = node.baseType.unifyNode;
-        
+
         if (!baseType.isInt)
             throw new WTypeError(node.origin.originString, "Base type of enum is not an integer: " + node.baseType);
-        
+
         for (let member of node.members) {
             if (!member.value)
                 continue;
-            
+
             let memberType = member.value.visit(this);
             if (!baseType.equalsWithCommit(memberType))
                 throw new WTypeError(member.origin.originString, "Type of enum member " + member.value.name + " does not patch enum base type (member type is " + memberType + ", enum base type is " + node.baseType + ")");
         }
-        
+
         let nextValue = baseType.defaultValue;
         for (let member of node.members) {
             if (member.value) {
                 nextValue = baseType.successorValue(member.value.unifyNode.valueForSelectedType);
                 continue;
             }
-            
+
             member.value = baseType.createLiteral(member.origin, nextValue);
             nextValue = baseType.successorValue(nextValue);
         }
-        
+
         let memberArray = Array.from(node.members);
         for (let i = 0; i < memberArray.length; ++i) {
             let member = memberArray[i];
@@ -236,7 +439,7 @@ class Checker extends Visitor {
                     throw new WTypeError(otherMember.origin.originString, "Duplicate enum member value (" + member.name + " has " + member.value + " while " + otherMember.name + " has " + otherMember.value + ")");
             }
         }
-        
+
         let foundZero = false;
         for (let member of node.members) {
             if (baseType.valuesEqual(member.value.unifyNode.valueForSelectedType, baseType.defaultValue)) {
@@ -247,7 +450,7 @@ class Checker extends Visitor {
         if (!foundZero)
             throw new WTypeError(node.origin.originString, "Enum does not have a member with the value zero");
     }
-    
+
     visitTypeRef(node)
     {
         if (!node.type)
@@ -306,20 +509,20 @@ class Checker extends Visitor {
         if (node.numColumnsValue != 2 && node.numColumnsValue != 3 && node.numColumnsValue != 4)
             throw new WTypeError(`${node.toString()}: ${node.numColumnsValue} is not 2, 3, or 4.`);
     }
-    
+
     visitArrayType(node)
     {
         node.elementType.visit(this);
-        
+
         if (!node.numElements.isConstexpr)
             throw new WTypeError(node.origin.originString, "Array length must be constexpr");
-        
+
         let type = node.numElements.visit(this);
-        
+
         if (!type.equalsWithCommit(this._program.intrinsics.uint))
             throw new WTypeError(node.origin.originString, "Array length must be a uint");
     }
-    
+
     visitVariableDecl(node)
     {
         node.type.visit(this);
@@ -330,7 +533,7 @@ class Checker extends Visitor {
                 throw new WTypeError(node.origin.originString, "Type mismatch in variable initialization: " + lhsType + " versus " + rhsType);
         }
     }
-    
+
     visitAssignment(node)
     {
         let lhsType = node.lhs.visit(this);
@@ -342,12 +545,12 @@ class Checker extends Visitor {
         node.type = lhsType;
         return lhsType;
     }
-    
+
     visitIdentityExpression(node)
     {
         return node.target.visit(this);
     }
-    
+
     visitReadModifyWriteExpression(node)
     {
         let lhsType = node.lValue.visit(this);
@@ -362,13 +565,13 @@ class Checker extends Visitor {
             return new WTypeError(node.origin.originString, "Type mismatch in read-modify-write: " + lhsType + " versus " + newValueType);
         return node.resultExp.visit(this);
     }
-    
+
     visitAnonymousVariable(node)
     {
         if (!node.type)
             throw new Error("Anonymous variable must know type before first appearance");
     }
-    
+
     visitDereferenceExpression(node)
     {
         let type = node.ptr.visit(this).unifyNode;
@@ -380,16 +583,16 @@ class Checker extends Visitor {
             throw new Error("Null address space in type: " + type);
         return node.type;
     }
-    
+
     visitMakePtrExpression(node)
     {
         let elementType = node.lValue.visit(this).unifyNode;
         if (!node.lValue.isLValue)
             throw new WTypeError(node.origin.originString, "Operand to & is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
-        
+
         return new PtrType(node.origin, node.lValue.addressSpace, elementType);
     }
-    
+
     visitMakeArrayRefExpression(node)
     {
         let elementType = node.lValue.visit(this).unifyNode;
@@ -397,37 +600,37 @@ class Checker extends Visitor {
             node.become(new ConvertPtrToArrayRefExpression(node.origin, node.lValue));
             return new ArrayRefType(node.origin, elementType.addressSpace, elementType.elementType);
         }
-        
+
         if (!node.lValue.isLValue)
             throw new WTypeError(node.origin.originString, "Operand to @ is not an LValue: " + node.lValue + node.lValue.notLValueReasonString);
-        
+
         if (elementType.isArray) {
             node.numElements = elementType.numElements;
             elementType = elementType.elementType;
         } else
             node.numElements = UintLiteral.withType(node.origin, 1, this._program.intrinsics.uint);
-            
+
         return new ArrayRefType(node.origin, node.lValue.addressSpace, elementType);
     }
-    
+
     visitConvertToArrayRefExpression(node)
     {
         throw new Error("Should not exist yet.");
     }
-    
+
     _finishVisitingPropertyAccess(node, baseType, extraArgs, extraArgTypes)
     {
         baseType = baseType.visit(new AutoWrapper())
         node.baseType = baseType;
-        
+
         // Such a type must exist. This may throw if it doesn't.
         let typeForAnd = baseType.argumentTypeForAndOverload(node.origin);
         if (!typeForAnd)
             throw new Error("Cannot get typeForAnd");
-        
+
         let errorForGet;
         let errorForAnd;
-        
+
         try {
             let result = CallExpression.resolve(
                 node.origin, node.possibleGetOverloads,
@@ -439,10 +642,10 @@ class Checker extends Visitor {
                 throw e;
             errorForGet = e;
         }
-        
+
         try {
             let baseForAnd = baseType.argumentForAndOverload(node.origin, node.base);
-            
+
             let result = CallExpression.resolve(
                 node.origin, node.possibleAndOverloads,
                 node.andFuncName, [baseForAnd, ...extraArgs], [typeForAnd, ...extraArgTypes],
@@ -454,7 +657,7 @@ class Checker extends Visitor {
                 throw e;
             errorForAnd = e;
         }
-        
+
         if (!node.resultTypeForGet && !node.resultTypeForAnd) {
             throw new WTypeError(
                 node.origin.originString,
@@ -463,11 +666,11 @@ class Checker extends Visitor {
                 "and tried by-pointer:\n" +
                 errorForAnd.typeErrorMessage);
         }
-        
+
         if (node.resultTypeForGet && node.resultTypeForAnd
             && !node.resultTypeForGet.equals(node.resultTypeForAnd))
             throw new WTypeError(node.origin.originString, "Result type resolved by-value (" + node.resultTypeForGet + ") does not match result type resolved by-pointer (" + node.resultTypeForAnd + ")");
-        
+
         try {
             let result = CallExpression.resolve(
                 node.origin, node.possibleSetOverloads,
@@ -480,7 +683,7 @@ class Checker extends Visitor {
                 throw e;
             node.errorForSet = e;
         }
-        
+
         // OK, now we need to determine if we are an lvalue. We are an lvalue if we can be assigned to. We can
         // be assigned to if we have an ander or setter. But it's weirder than that. We also need the base to be
         // an lvalue, except unless the base is an array reference.
@@ -498,31 +701,31 @@ class Checker extends Visitor {
             node.isLValue = true;
             node.addressSpace = node.base.isLValue ? node.base.addressSpace : baseType.addressSpace;
         }
-        
+
         return node.resultType;
     }
-    
+
     visitDotExpression(node)
     {
         let structType = node.struct.visit(this).unifyNode;
         return this._finishVisitingPropertyAccess(node, structType, [], []);
     }
-    
+
     visitIndexExpression(node)
     {
         let arrayType = node.array.visit(this).unifyNode;
         let indexType = node.index.visit(this);
-        
+
         return this._finishVisitingPropertyAccess(node, arrayType, [node.index], [indexType]);
     }
-    
+
     visitVariableRef(node)
     {
         if (!node.variable.type)
             throw new Error("Variable has no type: " + node.variable);
         return node.variable.type;
     }
-    
+
     visitReturn(node)
     {
         if (node.value) {
@@ -533,26 +736,26 @@ class Checker extends Visitor {
                 throw new WTypeError(node.origin.originString, "Trying to return " + resultType + " in a function that returns " + node.func.returnType);
             return;
         }
-        
+
         if (!node.func.returnType.equalsWithCommit(this._program.intrinsics.void))
             throw new WTypeError(node.origin.originString, "Non-void function must return a value");
     }
-    
+
     visitGenericLiteral(node)
     {
         return node.type;
     }
-    
+
     visitNullLiteral(node)
     {
         return node.type;
     }
-    
+
     visitBoolLiteral(node)
     {
         return this._program.intrinsics.bool;
     }
-    
+
     visitEnumLiteral(node)
     {
         return node.member.enumType;
@@ -614,14 +817,14 @@ class Checker extends Visitor {
     visitSwitchStatement(node)
     {
         let type = node.value.visit(this).commit();
-        
+
         if (!type.unifyNode.isInt && !(type.unifyNode instanceof EnumType))
             throw new WTypeError(node.origin.originString, "Cannot switch on non-integer/non-enum type: " + type);
 
         node.type = type;
-        
+
         let hasDefault = false;
-        
+
         for (let switchCase of node.switchCases) {
             switchCase.body.visit(this);
 
@@ -629,26 +832,26 @@ class Checker extends Visitor {
                 hasDefault = true;
                 continue;
             }
-            
+
             if (!switchCase.value.isConstexpr)
                 throw new WTypeError(switchCase.origin.originString, "Switch case not constexpr: " + switchCase.value);
-            
+
             let caseType = switchCase.value.visit(this);
             if (!type.equalsWithCommit(caseType))
                 throw new WTypeError(switchCase.origin.originString, "Switch case type does not match switch value type (case type is " + caseType + " but switch value type is " + type + ")");
         }
-        
+
         for (let i = 0; i < node.switchCases.length; ++i) {
             let firstCase = node.switchCases[i];
             for (let j = i + 1; j < node.switchCases.length; ++j) {
                 let secondCase = node.switchCases[j];
-                
+
                 if (firstCase.isDefault != secondCase.isDefault)
                     continue;
-                
+
                 if (firstCase.isDefault)
                     throw new WTypeError(secondCase.origin.originString, "Duplicate default case in switch statement");
-                
+
                 let valuesEqual = type.unifyNode.valuesEqual(
                     firstCase.value.unifyNode.valueForSelectedType,
                     secondCase.value.unifyNode.valueForSelectedType);
@@ -656,19 +859,19 @@ class Checker extends Visitor {
                     throw new WTypeError(secondCase.origin.originString, "Duplicate case in switch statement for value " + firstCase.value.unifyNode.valueForSelectedType);
             }
         }
-        
+
         if (!hasDefault) {
             let includedValues = new Set();
             for (let switchCase of node.switchCases)
                 includedValues.add(switchCase.value.unifyNode.valueForSelectedType);
-            
+
             for (let {value, name} of type.unifyNode.allValues()) {
                 if (!includedValues.has(value))
                     throw new WTypeError(node.origin.originString, "Value not handled by switch statement: " + name);
             }
         }
     }
-    
+
     visitCommaExpression(node)
     {
         let result = null;
@@ -687,7 +890,7 @@ class Checker extends Visitor {
         if (!elseType)
             throw new Error("Ternary expression else has no type: " + node.elseExpression);
         if (!bodyType.equalsWithCommit(elseType))
-            throw new WTypeError("Body and else clause of ternary statement don't have the same type: " + node);
+            throw new WTypeError(node.origin.originString, "Body and else clause of ternary statement don't have the same type: " + node);
         return bodyType;
     }
     
@@ -699,7 +902,7 @@ class Checker extends Visitor {
                 throw new Error("visitor returned null for " + argument);
             return newArgument.visit(new AutoWrapper());
         });
-        
+
         node.argumentTypes = argumentTypes;
         if (node.returnType)
             node.returnType.visit(this);
index 90d7ef8..312b00d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * 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
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 "use strict";
 
 class Field extends Node {
-    constructor(origin, name, type)
+    constructor(origin, name, type, semantic = null)
     {
         super();
         this._origin = origin;
         this._name = name;
         this._type = type;
+        this._semantic = semantic;
     }
     
     get origin() { return this._origin; }
     get name() { return this._name; }
     get type() { return this._type; }
+    get semantic() { return this._semantic; }
     
     toString()
     {
-        return this.type + " " + this.name;
+        let result = this.type + " " + this.name;
+        if (this.semantic)
+            result += ": " + this.semantic;
+        return result;
     }
 }
index 56b1d2c..e06b438 100644 (file)
@@ -25,7 +25,7 @@
 "use strict";
 
 class Func extends Node {
-    constructor(origin, name, returnType, parameters, isCast, shaderType, attributeBlock = null)
+    constructor(origin, name, returnType, parameters, isCast, shaderType = null, semantic = null, attributeBlock = null)
     {
         if (!(origin instanceof LexerToken))
             throw new Error("Bad origin: " + origin);
@@ -42,6 +42,7 @@ class Func extends Node {
         this._parameters = parameters;
         this._isCast = isCast;
         this._shaderType = shaderType;
+        this._semantic = semantic;
         this._attributeBlock = attributeBlock;
     }
     
@@ -52,6 +53,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 semantic() { return this._semantic; }
     get attributeBlock() { return this._attributeBlock; }
     get isEntryPoint() { return this.shaderType != null; }
     get returnTypeForOverloadResolution() { return this.isCast ? this.returnType : null; }
@@ -72,7 +74,10 @@ class Func extends Node {
             result += `operator ${this.returnType}`;
         else
             result += `${this.returnType} ${this.name}`;
-        return result + "(" + this.parameters + ")";
+        result += "(" + this.parameters + ")";
+        if (this.semantic)
+            result += ": " + this.semantic;
+        return result;
     }
     
     toString()
index c2d11f3..ef34ce4 100644 (file)
@@ -25,9 +25,9 @@
 "use strict";
 
 class FuncDef extends Func {
-    constructor(origin, name, returnType, parameters, body, isCast, shaderType, attributeBlock = null)
+    constructor(origin, name, returnType, parameters, body, isCast, shaderType = "", semantic = null, attributeBlock = null)
     {
-        super(origin, name, returnType, parameters, isCast, shaderType, attributeBlock);
+        super(origin, name, returnType, parameters, isCast, shaderType, semantic, attributeBlock);
         this._body = body;
         this.isRestricted = false;
     }
index d0bb1d6..05cdb52 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * 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
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 "use strict";
 
 class FuncParameter extends Value {
     // The name is optional. It's OK for it to be null!
-    constructor(origin, name, type)
+    constructor(origin, name, type, semantic = null)
     {
         super();
         this._origin = origin;
         this._name = name;
         this._type = type;
+        this._semantic = semantic;
     }
     
     get origin() { return this._origin; }
     get name() { return this._name; }
     get type() { return this._type; }
+    get semantic() { return this._semantic; }
     get varIsLValue() { return true; }
     
     toString()
     {
+        let result;
         if (!this.name)
-            return "" + this.type;
-        return this.type + " " + this.name;
+            result = "" + this.type;
+        else
+            result = this.type + " " + this.name;
+        if (this.semantic)
+            result += ": " + this.semantic;
+        return result;
     }
 }
 
index 04e4b78..9ed6546 100644 (file)
@@ -276,6 +276,7 @@ class Intrinsics {
                     type => {
                         this[`${textureType}<${typeArgument}>`] = type;
                         type.size = 1;
+                        type.isTexture = true;
                         type.populateDefaultValue = (buffer, offset) => buffer.set(offset, {});
                     });
                 for (let i = 2; i <= 4; ++i) {
@@ -284,6 +285,7 @@ class Intrinsics {
                         type => {
                             this[`${textureType}<${typeArgument}${i}>`] = type;
                             type.size = 1;
+                            type.isTexture = true;
                             type.populateDefaultValue = (buffer, offset) => buffer.set(offset, {});
                         });
                 }
index e54c7f3..47fd32c 100644 (file)
@@ -33,59 +33,5 @@ class LateChecker extends Visitor {
         if (!node.elementType.isPrimitive)
             throw new WTypeError(node.origin.originString, "Illegal pointer to non-primitive type: " + node);
     }
-    
-    _checkShaderType(node)
-    {
-        // FIXME: Tighten these checks. For now, we should only accept int32, uint32, float32, and float64.
-        let assertPrimitive = type => {
-            if (!type.isPrimitive)
-                throw new WTypeError(node.origin.originString, "Shader signature cannot include non-primitive type: " + type);
-        }
-        
-        if (node.shaderType == "vertex" || node.shaderType == "fragment") {
-            assertPrimitive(node.returnType);
-            // FIXME: Fragment shaders shouldn't have this restriction: https://bugs.webkit.org/show_bug.cgi?id=189387
-            if (!(node.returnType.unifyNode instanceof StructType))
-                throw new WTypeError(node.origin.originString, "Shader " + node.name + " must return a struct.");
-        }
-            
-        switch (node.shaderType) {
-        case "vertex":
-            for (let parameter of node.parameters) {
-                if (parameter.type.unifyNode instanceof StructType)
-                    assertPrimitive(parameter.type);
-                else if (!parameter.type.unifyNode.isArrayRef)
-                    throw new WTypeError(node.origin.originString, node.name + " accepts a parameter " + parameter.name + " which isn't a struct and isn't an ArrayRef.");
-            }
-            break;
-        case "fragment":
-            for (let parameter of node.parameters) {
-                if (parameter.name == "stageIn") {
-                    if (!(parameter.type.unifyNode instanceof StructType))
-                        throw new WTypeError(node.origin.originString, "Fragment entry points' stageIn parameter (of " + node.name + ") must be a struct type.");
-                    assertPrimitive(parameter.type);
-                } else {
-                    if (!parameter.type.unifyNode.isArrayRef)
-                        throw new WTypeError(node.origin.originString, "Fragment entry point's " + parameter.name + " parameter is not an array reference.");
-                }
-            }
-            break;
-        case "compute":
-            break;
-        case "compute":
-            break;
-        case "test":
-            break;
-        default:
-            throw new Error("Bad shader type: " + node.shaderType);
-        }
-    }
-    
-    visitFuncDef(node)
-    {
-        if (node.shaderType)
-            this._checkShaderType(node);
-        super.visitFuncDef(node);
-    }
 }
 
index 5b06918..bbf0285 100644 (file)
@@ -25,9 +25,9 @@
 "use strict";
 
 class NativeFunc extends Func {
-    constructor(origin, name, returnType, parameters, isCast, shaderType, stage = null)
+    constructor(origin, name, returnType, parameters, isCast, stage = null)
     {
-        super(origin, name, returnType, parameters, isCast, shaderType);
+        super(origin, name, returnType, parameters, isCast);
         this._stage = stage;
         this.isRestricted = false;
         this.implementation = null;
index 4a7a240..cd44d03 100644 (file)
@@ -29,7 +29,7 @@ function parse(program, origin, originKind, lineNumberOffset, text)
     let lexer = new Lexer(origin, originKind, lineNumberOffset, text);
 
     // The hardest part of dealing with C-like languages is parsing variable declaration statements.
-    // Let's consider if this happens in WSL. Here are the valid statements in WSL that being with an
+    // Let's consider if this happens in WHLSL. Here are the valid statements in WHLSL that being with an
     // identifier, if we assume that any expression can be a standalone statement.
     //
     //     x;
@@ -1085,7 +1085,13 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         if (type instanceof WSyntaxError)
             return type;
         let name = tryConsumeKind("identifier");
-        return new FuncParameter(type.origin, name ? name.text : null, type);
+        let semantic = null;
+        if (tryConsume(":")) {
+            semantic = parseSemantic();
+            if (semantic instanceof WSyntaxError)
+                return semantic;
+        }
+        return new FuncParameter(type.origin, name ? name.text : null, type, semantic);
     }
 
     function parseParameters()
@@ -1205,7 +1211,8 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         let returnType;
         let name;
         let isCast;
-        let shaderType;
+        let shaderType = null;
+        let semantic = null;
         let attributeBlock = null;
         let operatorToken = tryConsume("operator");
         if (operatorToken) {
@@ -1241,7 +1248,12 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         let parameters = parseParameters();
         if (parameters instanceof WSyntaxError)
             return parameters;
-        return new Func(origin, name, returnType, parameters, isCast, shaderType, attributeBlock);
+        if (tryConsume(":")) {
+            semantic = parseSemantic();
+            if (semantic instanceof WSyntaxError)
+                return semantic;
+        }
+        return new Func(origin, name, returnType, parameters, isCast, shaderType, semantic, attributeBlock);
     }
 
     function parseFuncDef()
@@ -1252,7 +1264,116 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         let body = parseBlock();
         if (body instanceof WSyntaxError)
             return body;
-        return new FuncDef(func.origin, func.name, func.returnType, func.parameters, body, func.isCast, func.shaderType, func.attributeBlock);
+        return new FuncDef(func.origin, func.name, func.returnType, func.parameters, body, func.isCast, func.shaderType, func.semantic, func.attributeBlock);
+    }
+
+    function parseStageInOutSemantic()
+    {
+        let origin = consume("attribute");
+        if (origin instanceof WSyntaxError)
+            return origin;
+        let maybeError = consume("(");
+        if (maybeError instanceof WSyntaxError)
+            return maybeError;
+        let index = consumeKind("intLiteral");
+        if (index instanceof WSyntaxError)
+            return index;
+        let uintVersion = index.text >>> 0;
+        if (uintVersion.toString() !== index.text)
+            return fail("Semantic  is not 32-bit unsigned integer: " + index.text);
+        maybeError = consume(")");
+        if (maybeError instanceof WSyntaxError)
+            return maybeError;
+        return new StageInOutSemantic(origin, uintVersion);
+    }
+
+    function parseResourceSemantic()
+    {
+        let origin = consume("register");
+        if (origin instanceof WSyntaxError)
+            return origin;
+        let maybeError = consume("(");
+        if (maybeError instanceof WSyntaxError)
+            return maybeError;
+        let mode = consumeKind("identifier");
+        if (mode instanceof WSyntaxError)
+            return mode;
+        mode = mode.text;
+        if (mode[0] != "u" && mode[0] != "t" && mode[0] != "b" && mode[0] != "s")
+            return fail("register needs to be either u, t, b, or s");
+        let indexText = mode.substring(1);
+        mode = mode[0];
+        let uintVersion = indexText >>> 0;
+        if (uintVersion.toString() !== indexText)
+            return fail("Register index is not 32-bit unsigned integer: " + indexText);
+        let space = 0;
+        if (tryConsume(",")) {
+            space = consumeKind("identifier");
+            if (space instanceof WSyntaxError)
+                return space;
+            space = space.text;
+            if (space.substring(0, "space".length) != "space")
+                return fail("Second argument to register() needs to be of the form \"space0\"");
+            let spaceText = space.substring("space".length);
+            let uintVersion = spaceText >>> 0;
+            if (uintVersion.toString() !== spaceText)
+                return fail("Register index is not 32-bit unsigned integer: " + spaceText);
+            space = uintVersion;
+        }
+        maybeError = consume(")");
+        if (maybeError instanceof WSyntaxError)
+            return maybeError;
+        return new ResourceSemantic(origin, mode, uintVersion, space);
+    }
+
+    function parseSpecializationConstantSemantic()
+    {
+        let origin = consume("specialized");
+        if (origin instanceof WSyntaxError)
+            return origin;
+        return new SpecializationConstantSemantic(origin);
+    }
+
+    function parseBuiltInSemantic()
+    {
+        let origin = tryConsume("SV_InstanceID",
+                                "SV_VertexID",
+                                "PSIZE",
+                                "SV_Position",
+                                "SV_IsFrontFace",
+                                "SV_SampleIndex",
+                                "SV_InnerCoverage",
+                                "SV_Depth",
+                                "SV_Coverage",
+                                "SV_DispatchThreadID",
+                                "SV_GroupID",
+                                "SV_GroupIndex",
+                                "SV_GroupThreadID");
+        if (origin)
+            return new BuiltInSemantic(origin, origin.text);
+        origin = consumeKind("identifier");
+        if (origin instanceof WSyntaxError)
+            return origin;
+        if (origin.text.substring(0, "SV_Target".length) == "SV_Target") {
+            let indexString = origin.text.substring("SV_Target".length);
+            let uintVersion = indexString >>> 0;
+            if (uintVersion.toString() !== indexString)
+                return fail("Semantic  is not 32-bit unsigned integer: " + indexString);
+            return new BuiltInSemantic(origin, "SV_Target", uintVersion);
+        } else
+            return fail(`Unknown semantic: ${origin.text}`);
+    }
+
+    function parseSemantic()
+    {
+        if (test("attribute"))
+            return parseStageInOutSemantic();
+        else if (test("register"))
+            return parseResourceSemantic();
+        else if (test("specialized"))
+            return parseSpecializationConstantSemantic();
+        else
+            return parseBuiltInSemantic();
     }
 
     function parseField()
@@ -1263,10 +1384,16 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         let name = consumeKind("identifier");
         if (name instanceof WSyntaxError)
             return name;
+        let semantic = null;
+        if (tryConsume(":")) {
+            semantic = parseSemantic();
+            if (semantic instanceof WSyntaxError)
+                return semantic;
+        }
         let maybeError = consume(";");
         if (maybeError instanceof WSyntaxError)
             return maybeError;
-        return new Field(name, name.text, type);
+        return new Field(name, name.text, type, semantic);
     }
 
     function parseStructType()
@@ -1300,7 +1427,9 @@ function parse(program, origin, originKind, lineNumberOffset, text)
         let maybeError = consume(";");
         if (maybeError instanceof WSyntaxError)
             return maybeError;
-        return new NativeFunc(func.origin, func.name, func.returnType, func.parameters, func.isCast, func.shaderType, stage);
+        if (func.shaderType || func.semantic)
+            return fail("Native functions can't be entry points or have semantics.");
+        return new NativeFunc(func.origin, func.name, func.returnType, func.parameters, func.isCast, stage);
     }
 
     function parseNative()
index c328892..8657c7f 100644 (file)
@@ -41,6 +41,10 @@ function programWithUnnecessaryThingsRemoved(program)
     nameFinder.add("void");
     nameFinder.add("bool");
     nameFinder.add("int");
+    nameFinder.add("uint");
+    nameFinder.add("float");
+    nameFinder.add("vector");
+    nameFinder.add("sampler");
     
     // Pull in things as necessary.
     while (nameFinder.worklist.length) {
diff --git a/Tools/WebGPUShadingLanguageRI/ResourceSemantic.js b/Tools/WebGPUShadingLanguageRI/ResourceSemantic.js
new file mode 100644 (file)
index 0000000..99a28a3
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * 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";
+
+class ResourceSemantic extends Semantic {
+    constructor(origin, resourceMode, index, space)
+    {
+         super(origin);
+         this._resourceMode = resourceMode;
+         this._index = index;
+         this._space = space;
+    }
+
+    get resourceMode() { return this._resourceMode; }
+    get index() { return this._index; }
+    get space() { return this._space; }
+
+    isAcceptableType(type, program)
+    {
+        switch (this.resourceMode) {
+        case "b":
+            if (type instanceof ReferenceType) {
+                return type.addressSpace == "constant";
+            } else
+                return type instanceof ArrayType;
+        case "u":
+            if (type instanceof ReferenceType) {
+                return type.addressSpace == "constant" || type.addressSpace == "device";
+            } else
+                return type.isTexture || type instanceof ArrayType;
+        case "t":
+            if (type instanceof ReferenceType) {
+                return type.addressSpace == "constant";
+            } else
+                return type.isTexture || type instanceof ArrayType;
+        case "s":
+            return type.equals(program.intrinsics.sampler);
+        default:
+            throw new Error(`Unknown resource mode: ${this.resourceMode}`);
+        }
+    }
+
+    isAcceptableForShaderType(direction, shaderType)
+    {
+        return direction == "input";
+    }
+
+    toString()
+    {
+        return `register(${this.resourceMode}${this.index}, space${this.space})`;
+    }
+}
+
index 3a14bda..48c6978 100644 (file)
@@ -60,7 +60,7 @@ class Rewriter {
     
     visitFuncParameter(node)
     {
-        let result = new FuncParameter(node.origin, node.name, node.type.visit(this));
+        let result = new FuncParameter(node.origin, node.name, node.type.visit(this), Node.visit(node.semantic, this));
         this._mapNode(node, result);
         result.ePtr = node.ePtr;
         return result;
@@ -105,7 +105,7 @@ class Rewriter {
     
     visitField(node)
     {
-        return new Field(node.origin, node.name, node.type.visit(this));
+        return new Field(node.origin, node.name, node.type.visit(this), Node.visit(node.semantic, this));
     }
     
     visitEnumMember(node)
@@ -400,5 +400,25 @@ class Rewriter {
         const matType = new MatrixType(node.origin, node.name, node.typeArguments.map(argument => argument.visit(this)));
         return matType;
     }
+
+    visitBuiltInSemantic(node)
+    {
+        return new BuiltInSemantic(node.origin, node.name, ...node.extraArguments);
+    }
+
+    visitResourceSemantic(node)
+    {
+        return new ResourceSemantic(node.origin, node.resourceMode, node.index, node.space);
+    }
+
+    visitStageInOutSemantic(node)
+    {
+        return new StageInOutSemantic(node.origin, node.index);
+    }
+
+    visitSpecializationConstantSemantic(node)
+    {
+        return new SpecializationConstantSemantic(node.origin);
+    }
 }
 
index ba6170b..6f6d6c0 100644 (file)
@@ -18,6 +18,7 @@ td {
     <script src="CreateLiteralType.js"></script>
     <script src="PropertyAccessExpression.js"></script>
     <script src="NativeType.js"></script>
+    <script src="Semantic.js"></script>
 
     <script src="AddressSpace.js"></script>
     <script src="AllocateAtEntryPoints.js"></script>
@@ -29,6 +30,7 @@ td {
     <script src="Block.js"></script>
     <script src="BoolLiteral.js"></script>
     <script src="Break.js"></script>
+    <script src="BuiltInSemantic.js"></script>
     <script src="BuiltinMatrixGetter.js"></script>
     <script src="BuiltinMatrixSetter.js"></script>
     <script src="BuiltinVectorGetter.js"></script>
@@ -124,10 +126,13 @@ td {
     <script src="ResolveOverloadImpl.js"></script>
     <script src="ResolveProperties.js"></script>
     <script src="ResolveTypeDefs.js"></script>
+    <script src="ResourceSemantic.js"></script>
     <script src="Return.js"></script>
     <script src="ReturnChecker.js"></script>
     <script src="ReturnException.js"></script>
     <script src="Sampler.js"></script>
+    <script src="SpecializationConstantSemantic.js"></script>
+    <script src="StageInOutSemantic.js"></script>
     <script src="StandardLibrary.js"></script>
     <script src="StatementCloner.js"></script>
     <script src="StructLayoutBuilder.js"></script>
diff --git a/Tools/WebGPUShadingLanguageRI/Semantic.js b/Tools/WebGPUShadingLanguageRI/Semantic.js
new file mode 100644 (file)
index 0000000..a178cf1
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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";
+
+class Semantic extends Node {
+    constructor(origin)
+    {
+        super();
+        this._origin = origin;
+    }
+
+    get origin() { return this._origin; }
+
+    equalToOtherSemantic(otherSemantic)
+    {
+        class Comparer extends Visitor {
+            visitBuiltInSemantic(node)
+            {
+                if (!(otherSemantic instanceof BuiltInSemantic))
+                    return false;
+                if (node.name != otherSemantic.name)
+                    return false;
+                if (node.extraArguments && otherSemantic.extraArguments) {
+                    if (node.extraArguments.length != otherSemantic.extraArguments.length)
+                        return false;
+                    for (let i = 0; i < node.extraArguments.length; ++i) {
+                        if (node.extraArguments[i] != otherSemantic.extraArguments[i])
+                            return false;
+                    }
+                    return true;
+                }
+                if (node.extraArguments)
+                    return node.extraArguments.length == 0;
+                if (otherSemantic.extraArguments)
+                    return otherSemantic.extraArguments.length == 0;
+                return true;
+            }
+
+            visitResourceSemantic(node)
+            {
+                if (!(otherSemantic instanceof ResourceSemantic))
+                    return false;
+                return node.resourceMode == otherSemantic.resourceMode && node.index == otherSemantic.index && node.space == otherSemantic.space;
+            }
+
+            visitStageInOutSemantic(node)
+            {
+                if (!(otherSemantic instanceof StageInOutSemantic))
+                    return false;
+                return node.index == otherSemantic.index;
+            }
+
+            visitSpecializationConstantSemantic(node)
+            {
+                return other instanceof SpecializationConstantSemantic;
+            }
+        }
+        return this.visit(new Comparer());
+    }
+}
+
diff --git a/Tools/WebGPUShadingLanguageRI/SpecializationConstantSemantic.js b/Tools/WebGPUShadingLanguageRI/SpecializationConstantSemantic.js
new file mode 100644 (file)
index 0000000..fd22069
--- /dev/null
@@ -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.
+ */
+"use strict";
+
+class SpecializationConstantSemantic extends Semantic {
+    constructor(origin)
+    {
+        super(origin);
+    }
+
+    isAcceptableType(type, program)
+    {
+        return type.isNumber;
+    }
+
+    isAcceptableForShaderType(direction, shaderType)
+    {
+        return direction == "input";
+    }
+
+    toString()
+    {
+        return "specialized";
+    }
+}
+
diff --git a/Tools/WebGPUShadingLanguageRI/StageInOutSemantic.js b/Tools/WebGPUShadingLanguageRI/StageInOutSemantic.js
new file mode 100644 (file)
index 0000000..be9eea9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * 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";
+
+class StageInOutSemantic extends Semantic {
+    constructor(origin, index)
+    {
+         super(origin);
+         this._index = index;
+    }
+
+    get index() { return this._index; }
+
+    isAcceptableType(type, program)
+    {
+        return type instanceof EnumType || type instanceof ArrayType || type instanceof VectorType || type instanceof MatrixType || type.isNumber;
+    }
+
+    isAcceptableForShaderType(direction, shaderType)
+    {
+        switch (shaderType) {
+        case "vertex":
+            return true;
+        case "fragment":
+            return direction == "input";
+        case "compute":
+            return false;
+        case "test":
+            return true;
+        default:
+            throw new Error(`Unknown shader type: ${shaderType}`);
+        }
+    }
+
+    toString()
+    {
+        return `attribute(${this.index})`;
+    }
+}
+
index c4bddc9..eca696e 100644 (file)
@@ -46,7 +46,7 @@ class StatementCloner extends Rewriter {
             node.origin, node.name,
             node.returnType.visit(this),
             node.parameters.map(parameter => parameter.visit(this)),
-            node.isCast, node.shaderType, node.stage);
+            node.isCast, node.stage);
         result.isRestricted = node.isRestricted;
         return result;
     }
index de8df5d..1a28158 100644 (file)
@@ -58,7 +58,7 @@ function synthesizeArrayOperatorLength(program)
         let nativeFunc = new NativeFunc(
             arrayType.origin, "operator.length", uint,
             [ new FuncParameter(arrayType.origin, null, paramType) ],
-            false, null);
+            false);
         nativeFunc.implementation = ([array]) => EPtr.box(arrayType.numElementsValue);
         program.add(nativeFunc);
 
index cf98c46..f39eaef 100644 (file)
@@ -52,7 +52,7 @@ function synthesizeCopyConstructorOperator(program)
     for (let type of types) {
         let nativeFunc = new NativeFunc(type.origin, "operator cast", TypeRef.wrap(type), [
             new FuncParameter(type.origin, null, TypeRef.wrap(type))
-        ], true, null);
+        ], true);
         nativeFunc.implementation = ([arg], node) => {
             let result = new EPtr(new EBuffer(type.size), 0);
             result.copyFrom(arg, type.size);
index dc50653..5bc5dd9 100644 (file)
@@ -62,7 +62,7 @@ function synthesizeDefaultConstructorOperator(program)
     program.visit(new FindAllTypes());
 
     for (let type of types) {
-        let nativeFunc = new NativeFunc(type.origin, "operator cast", TypeRef.wrap(type), [], true, null);
+        let nativeFunc = new NativeFunc(type.origin, "operator cast", TypeRef.wrap(type), [], true);
         nativeFunc.implementation = ([], node) => {
             let result = new EPtr(new EBuffer(type.size), 0);
             node.type.populateDefaultValue(result.buffer, 0);
index 137ac9f..8266a18 100644 (file)
@@ -32,7 +32,6 @@ function synthesizeEnumFunctions(program)
 
         let nativeFunc;
         let isCast = false;
-        let shaderType;
 
         nativeFunc = new NativeFunc(
             type.origin, "operator==", new TypeRef(type.origin, "bool"),
@@ -40,28 +39,28 @@ function synthesizeEnumFunctions(program)
                 new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name)),
                 new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name))
             ],
-            isCast, shaderType);
+            isCast);
         nativeFunc.implementation = ([left, right]) => EPtr.box(left.loadValue() == right.loadValue());
         program.add(nativeFunc);
 
         nativeFunc = new NativeFunc(
             type.origin, "operator.value", type.baseType.visit(new Rewriter()),
             [new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name))],
-            isCast, shaderType);
+            isCast);
         nativeFunc.implementation = ([value]) => value;
         program.add(nativeFunc);
 
         nativeFunc = new NativeFunc(
             type.origin, "operator cast", type.baseType.visit(new Rewriter()),
             [new FuncParameter(type.origin, null, new TypeRef(type.origin, type.name))],
-            isCast, shaderType);
+            isCast);
         nativeFunc.implementation = ([value]) => value;
         program.add(nativeFunc);
 
         nativeFunc = new NativeFunc(
             type.origin, "operator cast", new TypeRef(type.origin, type.name),
             [new FuncParameter(type.origin, null, type.baseType.visit(new Rewriter()))],
-            isCast, shaderType);
+            isCast);
         nativeFunc.implementation = ([value]) => value;
         program.add(nativeFunc);
     }
index 7859eeb..6c7ffd7 100644 (file)
@@ -49,13 +49,12 @@ function synthesizeStructAccessorsForStructType(program, type)
         }
 
         let isCast = false;
-        let shaderType;
         let nativeFunc;
 
         // The getter: operator.field
         nativeFunc = new NativeFunc(
             field.origin, "operator." + field.name, field.type,
-            [new FuncParameter(field.origin, null, TypeRef.wrap(type))], isCast, shaderType);
+            [new FuncParameter(field.origin, null, TypeRef.wrap(type))], isCast);
         setupImplementationData(nativeFunc, ([base], offset, structSize, fieldSize) => {
             let result = new EPtr(new EBuffer(fieldSize), 0);
             result.copyFrom(base.plus(offset), fieldSize);
@@ -70,7 +69,7 @@ function synthesizeStructAccessorsForStructType(program, type)
                 new FuncParameter(field.origin, null, TypeRef.wrap(type)),
                 new FuncParameter(field.origin, null, field.type)
             ],
-            isCast, shaderType);
+            isCast);
         setupImplementationData(nativeFunc, ([base, value], offset, structSize, fieldSize) => {
             let result = new EPtr(new EBuffer(structSize), 0);
             result.copyFrom(base, structSize);
@@ -89,7 +88,7 @@ function synthesizeStructAccessorsForStructType(program, type)
                         field.origin, null,
                         new PtrType(field.origin, addressSpace, TypeRef.wrap(type)))
                 ],
-                isCast, shaderType);
+                isCast);
             setupImplementationData(nativeFunc, ([base], offset, structSize, fieldSize) => {
                 base = base.loadValue();
                 if (!base)
index 0285fbe..cd3bda0 100644 (file)
@@ -1,6 +1,6 @@
 <html>
 <head>
-<title>WSL Test</title>
+<title>WHLSL Test</title>
 <script src="Node.js"></script>
 <script src="Type.js"></script>
 <script src="ReferenceType.js"></script>
@@ -12,6 +12,7 @@
 <script src="CreateLiteralType.js"></script>
 <script src="PropertyAccessExpression.js"></script>
 <script src="NativeType.js"></script>
+<script src="Semantic.js"></script>
 
 <script src="AddressSpace.js"></script>
 <script src="AllocateAtEntryPoints.js"></script>
@@ -23,6 +24,7 @@
 <script src="Block.js"></script>
 <script src="BoolLiteral.js"></script>
 <script src="Break.js"></script>
+<script src="BuiltInSemantic.js"></script>
 <script src="BuiltinMatrixGetter.js"></script>
 <script src="BuiltinMatrixSetter.js"></script>
 <script src="BuiltinVectorGetter.js"></script>
 <script src="ResolveOverloadImpl.js"></script>
 <script src="ResolveProperties.js"></script>
 <script src="ResolveTypeDefs.js"></script>
+<script src="ResourceSemantic.js"></script>
 <script src="Return.js"></script>
 <script src="ReturnChecker.js"></script>
 <script src="ReturnException.js"></script>
 <script src="Sampler.js"></script>
+<script src="SpecializationConstantSemantic.js"></script>
+<script src="StageInOutSemantic.js"></script>
 <script src="StandardLibrary.js"></script>
 <script src="StatementCloner.js"></script>
 <script src="StructLayoutBuilder.js"></script>
@@ -170,7 +175,7 @@ function doTestInBrowser()
             if (tester.next().done)
                 return;
         } catch (e) {
-            print("ERROR: " + e.message);
+            print("ERROR: " + (e.payload ? e.payload : e.message));
             print(e.stack);
             return;
         }
index 9e15b3e..659b432 100644 (file)
@@ -3107,133 +3107,262 @@ tests.typedefArray = function()
 
 tests.shaderTypes = function()
 {
+    doPrep(`
+        vertex float4 foo() : SV_Position {
+            return float4(0, 1, 2, 3);
+        }`);
+    doPrep(`
+        struct R {
+            float4 x : SV_Position;
+            int4 y : attribute(1);
+        }
+        vertex R foo() {
+            R z;
+            z.x = float4(1, 2, 3, 4);
+            z.y = int4(5, 6, 7, 8);
+            return z;
+        }`);
+    doPrep(`
+        struct R {
+            float4 x : SV_Position;
+            int4 y : attribute(1);
+        }
+        struct S {
+            R r;
+            float3 z : attribute(2);
+        }
+        vertex S foo() {
+            S w;
+            w.r.x = float4(1, 2, 3, 4);
+            w.r.y = int4(5, 6, 7, 8);
+            w.z = float3(9, 10, 11);
+            return w;
+        }`);
+    doPrep(`
+        vertex float4 foo(constant float* buffer : register(b0)) : SV_Position {
+            return float4(*buffer, *buffer, *buffer, *buffer);
+        }`);
+    doPrep(`
+        vertex float4 foo(constant float* buffer : register(b0, space0)) : SV_Position {
+            return float4(*buffer, *buffer, *buffer, *buffer);
+        }`);
+    doPrep(`
+        vertex float4 foo(constant float* buffer : register(b0, space1)) : SV_Position {
+            return float4(*buffer, *buffer, *buffer, *buffer);
+        }`);
+    doPrep(`
+        vertex float4 foo(constant float[] buffer : register(b0)) : SV_Position {
+            return float4(buffer[0], buffer[1], buffer[2], buffer[3]);
+        }`);
+    doPrep(`
+        vertex float4 foo(float[5] buffer : register(b0)) : SV_Position {
+            return float4(buffer[0], buffer[1], buffer[2], buffer[3]);
+        }`);
+    doPrep(`
+        vertex float4 foo(device float* buffer : register(u0)) : SV_Position {
+            return float4(*buffer, *buffer, *buffer, *buffer);
+        }`);
+    doPrep(`
+        vertex float4 foo(device float[] buffer : register(u0)) : SV_Position {
+            return float4(buffer[0], buffer[1], buffer[2], buffer[3]);
+        }`);
+    doPrep(`
+        vertex float4 foo(uint x : SV_InstanceID) : SV_Position {
+            return float4(float(x), float(x), float(x), float(x));
+        }`);
+    doPrep(`
+        fragment float4 foo(bool x : SV_IsFrontFace) : SV_Target0 {
+            return float4(1, 2, 3, 4);
+        }`);
+    doPrep(`
+        fragment float4 foo(int x : specialized) : SV_Target0 {
+            return float4(1, 2, 3, 4);
+        }`);
+    doPrep(`
+        fragment float4 foo(Texture1D<float4> t : register(t0), sampler s : register(s0)) : SV_Target0 {
+            return Sample(t, s, 0.4);
+        }`);
     checkFail(
         () => doPrep(`
-            struct Foo {
-                float4 x;
+            vertex void foo() : SV_Position {
             }
-            vertex Foo bar()
-            {
-                Foo result;
-                result.x = float4();
-                return result;
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            vertex float4 foo(float x : PSIZE) : SV_Position {
+                return float4(x, x, x, x);
             }
-            Foo foo() {
-                return bar();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            vertex float4 foo(int x : SV_InstanceID) : SV_Position {
+                return float4(float(x), float(x), float(x), float(x));
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            vertex float bar()
-            {
-                return 4.;
+            vertex float4 foo(float x) : SV_Position {
+                return float4(x, x, x, x);
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Foo {
-                float4 x;
+            fragment float4 foo(bool x : SV_IsFrontFace, bool y : SV_IsFrontFace) : SV_Target0 {
+                return float4(1, 2, 3, 4);
             }
-            vertex Foo bar(device Foo* x)
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            struct R {
+                float4 x : SV_Target0;
+            }
+            fragment R foo(bool x : SV_IsFrontFace) : SV_Depth {
+                R y;
+                y.x = float4(1, 2, 3, 4);
+                return y;
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Boo {
-                float4 x;
+            struct R {
+                bool x : SV_IsFrontFace;
             }
-            struct Foo {
-                float4 x;
-                device Boo* y;
+            fragment float4 foo(R x : SV_SampleIndex) : SV_Target0 {
+                return float4(1, 2, 3, 4);
             }
-            vertex Foo bar()
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            struct R {
+                bool x : SV_IsFrontFace;
+            }
+            struct S {
+                R r;
+                bool y : SV_IsFrontFace;
+            }
+            fragment float4 foo(S x) : SV_Target0 {
+                return float4(1, 2, 3, 4);
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Foo {
-                float4 x;
+            struct R {
+                float x : SV_IsFrontFace;
             }
-            struct Boo {
-                device Foo* y;
+            fragment float4 foo(R x) : SV_Target0 {
+                return float4(1, 2, 3, 4);
             }
-            vertex Foo bar(Boo b)
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            struct R {
+                float x : SV_IsFrontFace;
+            }
+            vertex uint foo() : SV_VertexID {
+                return 7;
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Foo {
-                float4 x;
+            typedef A = thread float*;
+            vertex float4 foo(device A[] x : register(u0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
-            vertex Foo bar(device Foo* x)
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            typedef A = thread float*;
+            vertex float4 foo(device A* x : register(u0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Foo {
-                float4 x;
+            typedef A = thread float*;
+            vertex float4 foo(A[4] x : register(u0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
-            fragment Foo bar(Foo foo)
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            enum Foo {
+                f, g
+            }
+            vertex float4 foo(Foo x : specialized) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Foo {
-                float4 x;
+            [numthreads(1, 1, 1)]
+            compute float foo() : attribute(0) {
+                return 5;
             }
-            fragment Foo bar(device Foo* stageIn)
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            fragment float foo() : attribute(0) {
+                return 5;
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Boo {
-                float4 x;
+            vertex float foo(device float* x : attribute(0)) : attribute(0) {
+                return 5;
             }
-            struct Foo {
-                float4 x;
-                device Boo* y;
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            vertex float4 foo(device float* x : register(b0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
-            fragment Boo bar(Foo stageIn)
-            {
-                return Boo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            vertex float4 foo(float x : register(b0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
     checkFail(
         () => doPrep(`
-            struct Boo {
-                float4 x;
+            vertex float4 foo(device float* x : register(t0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
-            struct Foo {
-                float4 x;
-                device Boo* y;
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            vertex float4 foo(Texture2D<float> x : register(b0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
-            fragment Foo bar(Boo stageIn)
-            {
-                return Foo();
+        `),
+        e => e instanceof WTypeError);
+    checkFail(
+        () => doPrep(`
+            vertex float4 foo(constant float[] x : register(s0)) : SV_Position {
+                return float4(1, 2, 3, 4);
             }
         `),
-        (e) => e instanceof WTypeError);
+        e => e instanceof WTypeError);
 }
 
 tests.vectorTypeSyntax = function()
@@ -5757,7 +5886,7 @@ tests.shaderStages = function()
 {
     doPrep(`
         struct Result {
-            float4 output;
+            float4 output : SV_Target0;
         }
         fragment Result foo()
         {
@@ -5781,7 +5910,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Position;
             }
             vertex Result foo()
             {
@@ -5797,7 +5926,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Position;
             }
             vertex Result foo()
             {
@@ -5811,7 +5940,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Target0;
             }
             fragment Result foo()
             {
@@ -5825,7 +5954,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Position;
             }
             vertex Result foo()
             {
@@ -5839,7 +5968,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Target0;
             }
             fragment Result foo()
             {
@@ -5853,7 +5982,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Position;
             }
             vertex Result foo()
             {
@@ -5867,7 +5996,7 @@ tests.shaderStages = function()
     checkFail(
         () => doPrep(`
             struct Result {
-                float4 output;
+                float4 output : SV_Target0;
             }
             fragment Result foo()
             {
@@ -6895,18 +7024,10 @@ tests.numThreads = function() {
         compute void foo() {
         }
 
-        [numthreads(6, 7, 8)]
-        compute void bar() {
-        }
-
-        [numthreads(9, 10, 11)]
-        compute void bar(device float[] buffer) {
-        }
-
         struct R {
-            float4 position;
+            float4 position : SV_Position;
         }
-        vertex R baz() {
+        vertex R bar() {
             R r;
             r.position = float4(1, 2, 3, 4);
             return r;
@@ -6925,45 +7046,10 @@ tests.numThreads = function() {
     if (foo.attributeBlock[0].z != 5)
         throw new Error("'foo' numthreads z is not 5");
 
-    if (program.functions.get("bar").length != 2)
+    if (program.functions.get("bar").length != 1)
         throw new Error("Cannot find function named 'bar'");
-    let bar1 = null;
-    let bar2 = null;
-    for (let bar of program.functions.get("bar")) {
-        if (bar.parameters.length == 0)
-            bar1 = bar;
-        else if (bar.parameters.length == 1)
-            bar2 = bar;
-        else
-            throw new Error("Unexpected 'bar' function.");
-    }
-    if (!bar1)
-        throw new Error("Could not find appropriate 'bar' function");
-    if (!bar2)
-        throw new Error("Could not find appropriate 'bar' function");
-
-    if (bar1.attributeBlock.length != 1)
-        throw new Error("'bar1' doesn't have numthreads attribute");
-    if (bar1.attributeBlock[0].x != 6)
-        throw new Error("'bar1' numthreads x is not 6");
-    if (bar1.attributeBlock[0].y != 7)
-        throw new Error("'bar1' numthreads y is not 7");
-    if (bar1.attributeBlock[0].z != 8)
-        throw new Error("'bar1' numthreads z is not 8");
-
-    if (bar2.attributeBlock.length != 1)
-        throw new Error("'bar2' doesn't have numthreads attribute");
-    if (bar2.attributeBlock[0].x != 9)
-        throw new Error("'bar2' numthreads x is not 9");
-    if (bar2.attributeBlock[0].y != 10)
-        throw new Error("'bar2' numthreads y is not 10");
-    if (bar2.attributeBlock[0].z != 11)
-        throw new Error("'bar2' numthreads z is not 11");
-
-    if (program.functions.get("baz").length != 1)
-        throw new Error("Cannot find function named 'baz'");
-    let baz = program.functions.get("baz")[0];
-    if (baz.attributeBlock != null)
+    let bar = program.functions.get("bar")[0];
+    if (bar.attributeBlock != null)
         throw new Error("'baz' has attribute block");
 
     checkFail(() => doPrep(`
@@ -9060,7 +9146,7 @@ tests.evaluationOrderForArguments = () => {
 
 tests.cannotCallAnotherEntryPoint = () => {
     checkFail(() => doPrep(`
-        struct Foo { int x; }
+        struct Foo { int x : attribute(0); }
         vertex Foo foo() { return bar(); }
         vertex Foo bar() { return Foo(); }
     `), e => e instanceof WTypeError && e.message.indexOf("it cannot be called from within an existing shader") !== -1);
index b8f4dbc..4ea1b9e 100644 (file)
@@ -40,11 +40,13 @@ class Visitor {
             for (let attribute of node.attributeBlock)
                 attribute.visit(this);
         }
+        Node.visit(node.semantic, this);
     }
     
     visitFuncParameter(node)
     {
         node.type.visit(this);
+        Node.visit(node.semantic, this);
     }
     
     visitFuncDef(node)
@@ -96,6 +98,7 @@ class Visitor {
     visitField(node)
     {
         node.type.visit(this);
+        Node.visit(node.semantic, this);
     }
     
     visitEnumType(node)
@@ -355,5 +358,21 @@ class Visitor {
     visitFuncNumThreadsAttribute(node)
     {
     }
+
+    visitBuiltInSemantic(node)
+    {
+    }
+
+    visitResourceSemantic(node)
+    {
+    }
+
+    visitStageInOutSemantic(node)
+    {
+    }
+
+    visitSpecializationConstantSemantic(node)
+    {
+    }
 }
 
index ae01100..b355624 100644 (file)
@@ -12,6 +12,7 @@
 <script src="CreateLiteralType.js"></script>
 <script src="PropertyAccessExpression.js"></script>
 <script src="NativeType.js"></script>
+<script src="Semantic.js"></script>
 
 <script src="AddressSpace.js"></script>
 <script src="AllocateAtEntryPoints.js"></script>
@@ -23,6 +24,7 @@
 <script src="Block.js"></script>
 <script src="BoolLiteral.js"></script>
 <script src="Break.js"></script>
+<script src="BuiltInSemantic.js"></script>
 <script src="BuiltinMatrixGetter.js"></script>
 <script src="BuiltinMatrixSetter.js"></script>
 <script src="BuiltinVectorGetter.js"></script>
 <script src="ResolveOverloadImpl.js"></script>
 <script src="ResolveProperties.js"></script>
 <script src="ResolveTypeDefs.js"></script>
+<script src="ResourceSemantic.js"></script>
 <script src="Return.js"></script>
 <script src="ReturnChecker.js"></script>
 <script src="ReturnException.js"></script>
 <script src="Sampler.js"></script>
+<script src="SpecializationConstantSemantic.js"></script>
+<script src="StageInOutSemantic.js"></script>
 <script src="StandardLibrary.js"></script>
 <script src="StatementCloner.js"></script>
 <script src="StructLayoutBuilder.js"></script>