Title: [222330] trunk/Tools
Revision
222330
Author
[email protected]
Date
2017-09-21 10:47:40 -0700 (Thu, 21 Sep 2017)

Log Message

WSL should support switch
https://bugs.webkit.org/show_bug.cgi?id=176978

Reviewed by Keith Miller.
        
This adds fairly complete support for switch statements. This includes things like verifying
whether or not a default statement is needed. This even works when the programmer lists all of
the values of an integer type.
        
To test that last part, this patch adds uint8 support. It's easy add it, but of course it
meant adding a lot of tests.
        
This also fixes enum bugs, since switch does things to enums that we previously didn't test.

* WebGPUShadingLanguageRI/All.js:
* WebGPUShadingLanguageRI/Checker.js:
(Checker.prototype.visitSwitchStatement):
* WebGPUShadingLanguageRI/EnumLiteral.js:
(EnumLiteral.prototype.get valueForSelectedType):
* WebGPUShadingLanguageRI/EnumType.js:
(EnumType.prototype.allValues):
(EnumType.prototype.valuesEqual):
* WebGPUShadingLanguageRI/Evaluator.js:
(Evaluator.prototype.visitSwitchStatement):
* WebGPUShadingLanguageRI/Inliner.js:
(Inliner.prototype.visitCallExpression):
(Inliner):
(Inliner.prototype.visitCastExpression): Deleted.
* WebGPUShadingLanguageRI/Intrinsics.js:
(Intrinsics.):
(Intrinsics):
* WebGPUShadingLanguageRI/LoopChecker.js:
(LoopChecker):
(LoopChecker.prototype.visitFuncDef):
(LoopChecker.prototype.visitWhileLoop):
(LoopChecker.prototype.visitDoWhileLoop):
(LoopChecker.prototype.visitForLoop):
(LoopChecker.prototype.visitSwitchStatement):
(LoopChecker.prototype.visitBreak):
(LoopChecker.prototype.visitContinue):
* WebGPUShadingLanguageRI/Node.js:
(Node.prototype.commit):
* WebGPUShadingLanguageRI/Parse.js:
(parseSwitchCase):
(parseSwitchStatement):
(parseStatement):
(parseBlockBody):
(parseBlock):
* WebGPUShadingLanguageRI/Prepare.js:
(let.prepare):
* WebGPUShadingLanguageRI/ReturnChecker.js:
(ReturnChecker.prototype._mergeReturnStyle):
(ReturnChecker.prototype.visitIfStatement):
(ReturnChecker.prototype.visitWhileLoop):
(ReturnChecker.prototype.visitDoWhileLoop):
(ReturnChecker.prototype.visitForLoop):
(ReturnChecker.prototype.visitSwitchStatement):
(ReturnChecker.prototype.visitContinue):
(ReturnChecker):
* WebGPUShadingLanguageRI/Rewriter.js:
(Rewriter.prototype.visitSwitchStatement):
(Rewriter.prototype.visitSwitchCase):
* WebGPUShadingLanguageRI/StandardLibrary.js:
(uint8.operator):
(bool.operator):
(_generateSwizzle):
* WebGPUShadingLanguageRI/SwitchCase.js: Added.
(SwitchCase):
(SwitchCase.prototype.get origin):
(SwitchCase.prototype.get isDefault):
(SwitchCase.prototype.get value):
(SwitchCase.prototype.get body):
(SwitchCase.prototype.toString):
* WebGPUShadingLanguageRI/SwitchStatement.js: Added.
(SwitchStatement):
(SwitchStatement.prototype.get origin):
(SwitchStatement.prototype.get value):
(SwitchStatement.prototype.add):
(SwitchStatement.prototype.get switchCases):
(SwitchStatement.prototype.toString):
* WebGPUShadingLanguageRI/Test.html:
* WebGPUShadingLanguageRI/Test.js:
(makeUint8):
(makeEnum):
(checkUint8):
(TEST_uint8SimpleMath):
(TEST_equality):
(TEST_notEquality):
(TEST_uint8BitAnd):
(TEST_uint8BitOr):
(TEST_uint8BitXor):
(TEST_uint8BitNot):
(TEST_uint8LShift):
(TEST_uint8RShift):
(TEST_enumWithExplicitIntBase):
(TEST_enumWithUintBase):
(TEST_enumFloatBase):
(TEST_enumStructBase):
(TEST_simpleSwitch):
(TEST_exhaustiveUint8Switch):
(TEST_notQuiteExhaustiveUint8Switch):
(TEST_switchFallThrough):
(TEST_switchBreak):
(TEST_enumSwitchBreakExhaustive):
(TEST_enumSwitchBreakNotQuiteExhaustive):
(doTest):
* WebGPUShadingLanguageRI/UnificationContext.js:
(UnificationContext):
(UnificationContext.prototype.addExtraNode):
(UnificationContext.prototype.get nodes):
* WebGPUShadingLanguageRI/Visitor.js:
(Visitor.prototype.visitProtocolDecl):
* WebGPUShadingLanguageRI/index.html:

Modified Paths

Added Paths

Diff

Modified: trunk/Tools/ChangeLog (222329 => 222330)


--- trunk/Tools/ChangeLog	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/ChangeLog	2017-09-21 17:47:40 UTC (rev 222330)
@@ -1,5 +1,121 @@
 2017-09-20  Filip Pizlo  <[email protected]>
 
+        WSL should support switch
+        https://bugs.webkit.org/show_bug.cgi?id=176978
+
+        Reviewed by Keith Miller.
+        
+        This adds fairly complete support for switch statements. This includes things like verifying
+        whether or not a default statement is needed. This even works when the programmer lists all of
+        the values of an integer type.
+        
+        To test that last part, this patch adds uint8 support. It's easy add it, but of course it
+        meant adding a lot of tests.
+        
+        This also fixes enum bugs, since switch does things to enums that we previously didn't test.
+
+        * WebGPUShadingLanguageRI/All.js:
+        * WebGPUShadingLanguageRI/Checker.js:
+        (Checker.prototype.visitSwitchStatement):
+        * WebGPUShadingLanguageRI/EnumLiteral.js:
+        (EnumLiteral.prototype.get valueForSelectedType):
+        * WebGPUShadingLanguageRI/EnumType.js:
+        (EnumType.prototype.allValues):
+        (EnumType.prototype.valuesEqual):
+        * WebGPUShadingLanguageRI/Evaluator.js:
+        (Evaluator.prototype.visitSwitchStatement):
+        * WebGPUShadingLanguageRI/Inliner.js:
+        (Inliner.prototype.visitCallExpression):
+        (Inliner):
+        (Inliner.prototype.visitCastExpression): Deleted.
+        * WebGPUShadingLanguageRI/Intrinsics.js:
+        (Intrinsics.):
+        (Intrinsics):
+        * WebGPUShadingLanguageRI/LoopChecker.js:
+        (LoopChecker):
+        (LoopChecker.prototype.visitFuncDef):
+        (LoopChecker.prototype.visitWhileLoop):
+        (LoopChecker.prototype.visitDoWhileLoop):
+        (LoopChecker.prototype.visitForLoop):
+        (LoopChecker.prototype.visitSwitchStatement):
+        (LoopChecker.prototype.visitBreak):
+        (LoopChecker.prototype.visitContinue):
+        * WebGPUShadingLanguageRI/Node.js:
+        (Node.prototype.commit):
+        * WebGPUShadingLanguageRI/Parse.js:
+        (parseSwitchCase):
+        (parseSwitchStatement):
+        (parseStatement):
+        (parseBlockBody):
+        (parseBlock):
+        * WebGPUShadingLanguageRI/Prepare.js:
+        (let.prepare):
+        * WebGPUShadingLanguageRI/ReturnChecker.js:
+        (ReturnChecker.prototype._mergeReturnStyle):
+        (ReturnChecker.prototype.visitIfStatement):
+        (ReturnChecker.prototype.visitWhileLoop):
+        (ReturnChecker.prototype.visitDoWhileLoop):
+        (ReturnChecker.prototype.visitForLoop):
+        (ReturnChecker.prototype.visitSwitchStatement):
+        (ReturnChecker.prototype.visitContinue):
+        (ReturnChecker):
+        * WebGPUShadingLanguageRI/Rewriter.js:
+        (Rewriter.prototype.visitSwitchStatement):
+        (Rewriter.prototype.visitSwitchCase):
+        * WebGPUShadingLanguageRI/StandardLibrary.js:
+        (uint8.operator):
+        (bool.operator):
+        (_generateSwizzle):
+        * WebGPUShadingLanguageRI/SwitchCase.js: Added.
+        (SwitchCase):
+        (SwitchCase.prototype.get origin):
+        (SwitchCase.prototype.get isDefault):
+        (SwitchCase.prototype.get value):
+        (SwitchCase.prototype.get body):
+        (SwitchCase.prototype.toString):
+        * WebGPUShadingLanguageRI/SwitchStatement.js: Added.
+        (SwitchStatement):
+        (SwitchStatement.prototype.get origin):
+        (SwitchStatement.prototype.get value):
+        (SwitchStatement.prototype.add):
+        (SwitchStatement.prototype.get switchCases):
+        (SwitchStatement.prototype.toString):
+        * WebGPUShadingLanguageRI/Test.html:
+        * WebGPUShadingLanguageRI/Test.js:
+        (makeUint8):
+        (makeEnum):
+        (checkUint8):
+        (TEST_uint8SimpleMath):
+        (TEST_equality):
+        (TEST_notEquality):
+        (TEST_uint8BitAnd):
+        (TEST_uint8BitOr):
+        (TEST_uint8BitXor):
+        (TEST_uint8BitNot):
+        (TEST_uint8LShift):
+        (TEST_uint8RShift):
+        (TEST_enumWithExplicitIntBase):
+        (TEST_enumWithUintBase):
+        (TEST_enumFloatBase):
+        (TEST_enumStructBase):
+        (TEST_simpleSwitch):
+        (TEST_exhaustiveUint8Switch):
+        (TEST_notQuiteExhaustiveUint8Switch):
+        (TEST_switchFallThrough):
+        (TEST_switchBreak):
+        (TEST_enumSwitchBreakExhaustive):
+        (TEST_enumSwitchBreakNotQuiteExhaustive):
+        (doTest):
+        * WebGPUShadingLanguageRI/UnificationContext.js:
+        (UnificationContext):
+        (UnificationContext.prototype.addExtraNode):
+        (UnificationContext.prototype.get nodes):
+        * WebGPUShadingLanguageRI/Visitor.js:
+        (Visitor.prototype.visitProtocolDecl):
+        * WebGPUShadingLanguageRI/index.html:
+
+2017-09-20  Filip Pizlo  <[email protected]>
+
         WSL needs a way to verify that structs are not cyclic
         https://bugs.webkit.org/show_bug.cgi?id=177044
 

Modified: trunk/Tools/WebGPUShadingLanguageRI/All.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/All.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/All.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -145,6 +145,8 @@
 load("StructLayoutBuilder.js");
 load("StructType.js");
 load("Substitution.js");
+load("SwitchCase.js");
+load("SwitchStatement.js");
 load("SynthesizeEnumFunctions.js");
 load("SynthesizeStructAccessors.js");
 load("TrapStatement.js");

Modified: trunk/Tools/WebGPUShadingLanguageRI/Checker.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Checker.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Checker.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -521,6 +521,64 @@
             node.increment.visit(this);
         node.body.visit(this);
     }
+
+    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);
+
+            if (switchCase.isDefault) {
+                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);
+                if (valuesEqual)
+                    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)
     {

Modified: trunk/Tools/WebGPUShadingLanguageRI/EnumLiteral.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/EnumLiteral.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/EnumLiteral.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -41,6 +41,11 @@
             return false;
         return this.member == other.member;
     }
+    
+    get valueForSelectedType()
+    {
+        return this.member.value.unifyNode.valueForSelectedType;
+    }
         
     toString()
     {

Modified: trunk/Tools/WebGPUShadingLanguageRI/EnumType.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/EnumType.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/EnumType.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -53,6 +53,17 @@
     
     get isPrimitive() { return true; }
     
+    *allValues()
+    {
+        for (let member of this.members)
+            yield {value: member.value.unifyNode.valueForSelectedType, name: member.name};
+    }
+    
+    valuesEqual(a, b)
+    {
+        return this.baseType.unifyNode.valuesEqual(a, b);
+    }
+    
     populateDefaultValue(buffer, offset)
     {
         this.baseType.populateDefaultValue(buffer, offset);

Modified: trunk/Tools/WebGPUShadingLanguageRI/Evaluator.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Evaluator.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Evaluator.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -243,6 +243,41 @@
             }
         }
     }
+    
+    visitSwitchStatement(node)
+    {
+        let findAndRunCast = predicate => {
+            for (let i = 0; i < node.switchCases.length; ++i) {
+                let switchCase = node.switchCases[i];
+                if (predicate(switchCase)) {
+                    try {
+                        for (let j = i; j < node.switchCases.length; ++j)
+                            node.switchCases[j].visit(this);
+                    } catch (e) {
+                        if (e != BreakException)
+                            throw e;
+                    }
+                    return true;
+                }
+            }
+            return false;
+        };
+        
+        let value = node.value.visit(this).loadValue();
+        
+        let found = findAndRunCast(switchCase => {
+            if (switchCase.isDefault)
+                return false;
+            return node.type.unifyNode.valuesEqual(
+                value, switchCase.value.unifyNode.valueForSelectedType);
+        });
+        if (found)
+            return;
+        
+        found = findAndRunCast(switchCase => switchCase.isDefault);
+        if (!found)
+            throw new Error("Switch statement did not find case");
+    }
 
     visitBreak(node)
     {

Modified: trunk/Tools/WebGPUShadingLanguageRI/Inliner.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Inliner.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Inliner.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -48,10 +48,5 @@
             return resultingBlock;
         });
     }
-
-    visitCastExpression(node)
-    {
-        return this.visitCallExpression(node);
-    }
 }
 

Modified: trunk/Tools/WebGPUShadingLanguageRI/Intrinsics.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Intrinsics.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Intrinsics.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -76,6 +76,12 @@
                 type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
                 type.formatValueFromIntLiteral = value => value | 0;
                 type.formatValueFromUintLiteral = value => value | 0;
+                type.allValues = function*() { 
+                    for (let i = 0; i <= 0xffffffff; ++i) {
+                        let value = i | 0;
+                        yield {value: value, name: value};
+                    }
+                };
             });
 
         this._map.set(
@@ -94,9 +100,35 @@
                 type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
                 type.formatValueFromIntLiteral = value => value >>> 0;
                 type.formatValueFromUintLiteral = value => value >>> 0;
+                type.allValues = function*() { 
+                    for (let i = 0; i <= 0xffffffff; ++i)
+                        yield {value: i, name: i};
+                };
             });
 
         this._map.set(
+            "native Primitive type uint8<>",
+            type => {
+                this.uint8 = type;
+                type.isInt = true;
+                type.isNumber = true;
+                type.isSigned = false;
+                type.canRepresent = value => isBitwiseEquivalent(value & 0xff, value);
+                type.size = 1;
+                type.defaultValue = 0;
+                type.createLiteral = (origin, value) => IntLiteral.withType(origin, value & 0xff, type);
+                type.successorValue = value => (value + 1) & 0xff;
+                type.valuesEqual = (a, b) => a === b;
+                type.populateDefaultValue = (buffer, offset) => buffer.set(offset, 0);
+                type.formatValueFromIntLiteral = value => value & 0xff;
+                type.formatValueFromUintLiteral = value => value & 0xff;
+                type.allValues = function*() {
+                    for (let i = 0; i <= 0xff; ++i)
+                        yield {value: i, name: i};
+                };
+            });
+
+        this._map.set(
             "native Primitive type float<>",
             type => {
                 this.float = type;
@@ -141,6 +173,12 @@
             });
         
         this._map.set(
+            "native operator int32<>(uint8)",
+            func => {
+                func.implementation = ([value]) => EPtr.box(value.loadValue() | 0);
+            });
+        
+        this._map.set(
             "native operator uint32<>(int32)",
             func => {
                 func.implementation = ([value]) => EPtr.box(value.loadValue() >>> 0);
@@ -147,6 +185,24 @@
             });
         
         this._map.set(
+            "native operator uint32<>(uint8)",
+            func => {
+                func.implementation = ([value]) => EPtr.box(value.loadValue() >>> 0);
+            });
+        
+        this._map.set(
+            "native operator uint8<>(int32)",
+            func => {
+                func.implementation = ([value]) => EPtr.box(value.loadValue() & 0xff);
+            });
+        
+        this._map.set(
+            "native operator uint8<>(uint32)",
+            func => {
+                func.implementation = ([value]) => EPtr.box(value.loadValue() & 0xff);
+            });
+        
+        this._map.set(
             "native operator float<>(double)",
             func => {
                 func.implementation = ([value]) => EPtr.box(Math.fround(value.loadValue()));

Modified: trunk/Tools/WebGPUShadingLanguageRI/LoopChecker.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/LoopChecker.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/LoopChecker.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -29,13 +29,13 @@
     {
         super();
         this._loopDepth = 0;
+        this._switchDepth = 0;
     }
 
     visitFuncDef(node)
     {
-        if (this._loopDepth != 0) {
-            throw new Error("LoopChecker does not understand nested functions.");
-        }
+        if (this._loopDepth != 0)
+            throw new WTypeError(node.origin.originString, "LoopChecker does not understand nested functions.");
         super.visitFuncDef(node);
     }
 
@@ -44,9 +44,8 @@
         node.conditional.visit(this);
         ++this._loopDepth;
         node.body.visit(this);
-        if (this._loopDepth == 0) {
-            throw new Error("The number of nested loops is negative!");
-        }
+        if (this._loopDepth == 0)
+            throw new WTypeError(node.origin.originString, "The number of nested loops is negative!");
         --this._loopDepth;
     }
 
@@ -54,9 +53,8 @@
     {
         ++this._loopDepth;
         node.body.visit(this);
-        if (this._loopDepth == 0) {
-            throw new Error("The number of nested loops is negative!");
-        }
+        if (this._loopDepth == 0)
+            throw new WTypeError(node.origin.originString, "The number of nested loops is negative!");
         --this._loopDepth;
         node.conditional.visit(this);
     }
@@ -71,25 +69,31 @@
             node.increment.visit(this);
         ++this._loopDepth;
         node.body.visit(this);
-        if (this._loopDepth == 0) {
-            throw new Error("The number of nested loops is negative!");
-        }
+        if (this._loopDepth == 0)
+            throw new WTypeError(node.origin.originString, "The number of nested loops is negative!");
         --this._loopDepth;
     }
     
+    visitSwitchStatement(node)
+    {
+        node.value.visit(this);
+        this._switchDepth++;
+        for (let switchCase of node.switchCases)
+            switchCase.visit(this);
+        this._switchDepth--;
+    }
+    
     visitBreak(node)
     {
-        if (this._loopDepth == 0) {
-            throw new WError("Break statement without enclosing loop!");
-        }
+        if (this._loopDepth == 0 && this._switchDepth == 0)
+            throw new WTypeError(node.origin.originString, "Break statement without enclosing loop or switch!");
         super.visitBreak(node);
     }
     
     visitContinue(node)
     {
-        if (this._loopDepth == 0) {
-            throw new WError("Continue statement without enclosing loop!");
-        }
+        if (this._loopDepth == 0)
+            throw new WTypeError(node.origin.originString, "Continue statement without enclosing loop!");
         super.visitContinue(node);
     }
 }

Modified: trunk/Tools/WebGPUShadingLanguageRI/Node.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Node.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Node.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -108,6 +108,17 @@
         return unificationContext;
     }
     
+    commit()
+    {
+        let unificationContext = new UnificationContext();
+        unificationContext.addExtraNode(this);
+        let result = unificationContext.verify();
+        if (!result.result)
+            throw new WError(node.origin.originString, "Could not infer type: " + result.reason);
+        unificationContext.commit();
+        return unificationContext.find(this);
+    }
+    
     substitute(parameters, argumentList)
     {
         return this.visit(new Substitution(parameters, argumentList));

Modified: trunk/Tools/WebGPUShadingLanguageRI/Parse.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Parse.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Parse.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -731,6 +731,30 @@
         return new CommaExpression(type.origin, list);
     }
     
+    function parseSwitchCase()
+    {
+        let token = consume("default", "case");
+        let value;
+        if (token.text == "case")
+            value = parseConstexpr();
+        consume(":");
+        let body = parseBlockBody("}", "default", "case");
+        return new SwitchCase(token, value, body);
+    }
+    
+    function parseSwitchStatement()
+    {
+        let origin = consume("switch");
+        consume("(");
+        let value = parseExpression();
+        consume(")");
+        consume("{");
+        let result = new SwitchStatement(origin, value);
+        while (!tryConsume("}"))
+            result.add(parseSwitchCase());
+        return result;
+    }
+    
     function parseStatement()
     {
         let token = lexer.peek();
@@ -752,6 +776,8 @@
             return parseFor();
         if (token.text == "if")
             return parseIfStatement();
+        if (token.text == "switch")
+            return parseSwitchStatement();
         if (token.text == "trap") {
             let origin = consume("trap");
             consume(";");
@@ -765,15 +791,21 @@
         return parseEffectfulStatement();
     }
     
-    function parseBlock()
+    function parseBlockBody(...terminators)
     {
-        let origin = consume("{");
         let block = new Block(origin);
-        while (!test("}")) {
+        while (!test(...terminators)) {
             let statement = parseStatement();
             if (statement)
                 block.add(statement);
         }
+        return block;
+    }
+    
+    function parseBlock()
+    {
+        let origin = consume("{");
+        let block = parseBlockBody("}");
         consume("}");
         return block;
     }

Modified: trunk/Tools/WebGPUShadingLanguageRI/Prepare.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Prepare.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Prepare.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -32,11 +32,9 @@
             parse(standardProgram, "/internal/stdlib", "native", 72, standardLibrary);
         }
         
-        if (!arguments.length)
-            return;
-        
         let program = cloneProgram(standardProgram);
-        parse(program, origin, "user", lineNumberOffset, text);
+        if (arguments.length)
+            parse(program, origin, "user", lineNumberOffset, text);
         program = programWithUnnecessaryThingsRemoved(program);
         
         foldConstexprs(program);

Modified: trunk/Tools/WebGPUShadingLanguageRI/ReturnChecker.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/ReturnChecker.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/ReturnChecker.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -35,6 +35,25 @@
         };
         this._program = program;
     }
+    
+    _mergeReturnStyle(a, b)
+    {
+        if (!a)
+            return b;
+        if (!b)
+            return a;
+        switch (a) {
+        case this.returnStyle.DefinitelyReturns:
+        case this.returnStyle.DefinitelyDoesntReturn:
+            if (a == b)
+                return a;
+            return this.returnStyle.HasntReturnedYet;
+        case this.returnStyle.HasntReturnedYet:
+            return this.returnStyle.HasntReturnedYet;
+        default:
+            throw new Error("Bad return style: " + a);
+        }
+    }
 
     visitFuncDef(node)
     {
@@ -66,10 +85,7 @@
         if (node.elseBody) {
             let bodyValue = node.body.visit(this);
             let elseValue = node.elseBody.visit(this);
-            if (bodyValue == this.returnStyle.DefinitelyReturns && elseValue == this.returnStyle.DefinitelyReturns)
-                return this.returnStyle.DefinitelyReturns;
-            if (bodyValue == this.returnStyle.DefinitelyDoesntReturn && elseValue == this.returnStyle.DefinitelyDoesntReturn)
-                return this.returnStyle.DefinitelyDoesntReturn;
+            return this._mergeReturnStyle(bodyValue, elseValue);
         }
         return this.returnStyle.HasntReturnedYet;
     }
@@ -81,7 +97,6 @@
 
     visitWhileLoop(node)
     {
-        node.conditional.visit(this);
         let bodyReturn = node.body.visit(this);
         if (node.conditional instanceof CallExpression && this._isBoolCastFromLiteralTrue(node.conditional)) {
             switch (bodyReturn) {
@@ -105,18 +120,11 @@
         case this.returnStyle.HasntReturnedYet:
             result = this.returnStyle.HasntReturnedYet;
         }
-        node.conditional.visit(this);
         return result;
     }
 
     visitForLoop(node)
     {
-        if (node.initialization)
-            node.initialization.visit(this);
-        if (node.condition)
-            node.condition.visit(this);
-        if (node.increment)
-            node.increment.visit(this);
         let bodyReturn = node.body.visit(this);
         if (node.condition === undefined || this._isBoolCastFromLiteralTrue(node.condition)) {
             switch (bodyReturn) {
@@ -129,6 +137,32 @@
         }
     }
     
+    visitSwitchStatement(node)
+    {
+        // FIXME: This seems like it's missing things. For example, we need to be smart about this:
+        //
+        // for (;;) {
+        //     switch (...) {
+        //     ...
+        //         continue; // This continues the for loop
+        //     }
+        // }
+        //
+        // I'm not sure what that means for this analysis. I'm starting to think that the right way to
+        // build this analysis is to run a visitor that builds a CFG and then analyze the CFG.
+        // https://bugs.webkit.org/show_bug.cgi?id=177172
+        
+        let returnStyle = null;
+        for (let switchCase of node.switchCases) {
+            let bodyStyle = switchCase.body.visit(this);
+            // FIXME: The fact that we need this demonstrates the need for CFG analysis.
+            if (bodyStyle == this.returnStyle.DefinitelyDoesntReturn)
+                bodyStyle = this.returnStyle.HasntReturnedYet;
+            returnStyle = this._mergeReturnStyle(returnStyle, bodyStyle);
+        }
+        return returnStyle;
+    }
+    
     visitReturn(node)
     {
         return this.returnStyle.DefinitelyReturns;
@@ -146,6 +180,12 @@
 
     visitContinue(node)
     {
+        // FIXME: This seems wrong. Consider a loop like:
+        //
+        // int foo() { for (;;) { continue; } }
+        //
+        // This program shouldn't claim that the problem is that it doesn't return.
+        // https://bugs.webkit.org/show_bug.cgi?id=177172
         return this.returnStyle.DefinitelyDoesntReturn;
     }
 }

Modified: trunk/Tools/WebGPUShadingLanguageRI/Rewriter.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Rewriter.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Rewriter.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -394,6 +394,20 @@
             node.body.visit(this));
     }
     
+    visitSwitchStatement(node)
+    {
+        let result = new SwitchStatement(node.origin, Node.visit(node.value, this));
+        for (let switchCase of node.switchCases)
+            result.add(switchCase.visit(this));
+        result.type = Node.visit(node.type, this);
+        return result;
+    }
+    
+    visitSwitchCase(node)
+    {
+        return new SwitchCase(node.origin, Node.visit(node.value, this), node.body.visit(this));
+    }
+    
     visitAnonymousVariable(node)
     {
         let result = new AnonymousVariable(node.origin, node.type.visit(this));

Modified: trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -40,36 +40,8 @@
     }
 }
 
-// There are 481 swizzle operators. Let's not list them explicitly.
-function _generateSwizzle(maxDepth, maxItems, array)
-{
-    if (!array)
-        array = [];
-    if (array.length == maxDepth) {
-        let result = `vec${array.length}<T> operator.${array.join("")}<T>(vec${maxItems}<T> v)
-{
-    vec${array.length}<T> result;
-`;
-        for (let i = 0; i < array.length; ++i) {
-            result += `    result.${intToString(i)} = v.${array[i]};
-`;
-        }
-        result += `    return result;
-}
-`;
-        return result;
-    }
-    let result = "";
-    for (let i = 0; i < maxItems; ++i) {
-        array.push(intToString(i));
-        result += _generateSwizzle(maxDepth, maxItems, array);
-        array.pop();
-    }
-    return result;
-}
-
 // NOTE: The next line is line 72, and we rely on this in Prepare.js.
-const standardLibrary = `
+let standardLibrary = `
 // This is the WSL standard library. Implementations of all of these things are in
 // Intrinsics.js. The only thing that gets defined before we get here is the Primitive
 // protocol.
@@ -77,6 +49,7 @@
 // Need to bootstrap void first.
 native Primitive typedef void;
 
+native Primitive typedef uint8;
 native Primitive typedef int32;
 native Primitive typedef uint32;
 native Primitive typedef bool;
@@ -87,28 +60,38 @@
 native Primitive typedef double;
 
 native operator int32(uint32);
+native operator int32(uint8);
 native operator uint32(int32);
+native operator uint32(uint8);
+native operator uint8(int32);
+native operator uint8(uint32);
 native operator float(double);
 native operator double(float);
 
 native int operator+(int, int);
 native uint operator+(uint, uint);
+uint8 operator+(uint8 a, uint8 b) { return uint8(uint(a) + uint(b)); }
 native float operator+(float, float);
 native double operator+(double, double);
 int operator++(int value) { return value + 1; }
 uint operator++(uint value) { return value + 1; }
+uint8 operator++(uint8 value) { return value + 1; }
 native int operator-(int, int);
 native uint operator-(uint, uint);
+uint8 operator-(uint8 a, uint8 b) { return uint8(uint(a) - uint(b)); }
 native float operator-(float, float);
 native double operator-(double, double);
 int operator--(int value) { return value - 1; }
 uint operator--(uint value) { return value - 1; }
+uint8 operator--(uint8 value) { return value - 1; }
 native int operator*(int, int);
 native uint operator*(uint, uint);
+uint8 operator*(uint8 a, uint8 b) { return uint8(uint(a) * uint(b)); }
 native float operator*(float, float);
 native double operator*(double, double);
 native int operator/(int, int);
 native uint operator/(uint, uint);
+uint8 operator/(uint8 a, uint8 b) { return uint8(uint(a) / uint(b)); }
 native int operator&(int, int);
 native int operator|(int, int);
 native int operator^(int, int);
@@ -121,27 +104,38 @@
 native uint operator~(uint);
 native uint operator<<(uint, uint);
 native uint operator>>(uint, uint);
+uint8 operator&(uint8 a, uint8 b) { return uint8(uint(a) & uint(b)); }
+uint8 operator|(uint8 a, uint8 b) { return uint8(uint(a) | uint(b)); }
+uint8 operator^(uint8 a, uint8 b) { return uint8(uint(a) ^ uint(b)); }
+uint8 operator~(uint8 value) { return uint8(~uint(value)); }
+uint8 operator<<(uint8 a, uint b) { return uint8(uint(a) << (b & 7)); }
+uint8 operator>>(uint8 a, uint b) { return uint8(uint(a) >> (b & 7)); }
 native float operator/(float, float);
 native double operator/(double, double);
 native bool operator==(int, int);
 native bool operator==(uint, uint);
+bool operator==(uint8 a, uint8 b) { return uint(a) == uint(b); }
 native bool operator==(bool, bool);
 native bool operator==(float, float);
 native bool operator==(double, double);
 native bool operator<(int, int);
 native bool operator<(uint, uint);
+bool operator<(uint8 a, uint8 b) { return uint(a) < uint(b); }
 native bool operator<(float, float);
 native bool operator<(double, double);
 native bool operator<=(int, int);
 native bool operator<=(uint, uint);
+bool operator<=(uint8 a, uint8 b) { return uint(a) <= uint(b); }
 native bool operator<=(float, float);
 native bool operator<=(double, double);
 native bool operator>(int, int);
 native bool operator>(uint, uint);
+bool operator>(uint8 a, uint8 b) { return uint(a) > uint(b); }
 native bool operator>(float, float);
 native bool operator>(double, double);
 native bool operator>=(int, int);
 native bool operator>=(uint, uint);
+bool operator>=(uint8 a, uint8 b) { return uint(a) >= uint(b); }
 native bool operator>=(float, float);
 native bool operator>=(double, double);
 
@@ -397,14 +391,37 @@
 {
     return length;
 }
+`;
 
-${_generateSwizzle(2, 2)}
-${_generateSwizzle(3, 2)}
-${_generateSwizzle(4, 2)}
-${_generateSwizzle(2, 3)}
-${_generateSwizzle(3, 3)}
-${_generateSwizzle(4, 3)}
-${_generateSwizzle(2, 4)}
-${_generateSwizzle(3, 4)}
-${_generateSwizzle(4, 4)}
+// There are 481 swizzle operators. Let's not list them explicitly.
+function _generateSwizzle(maxDepth, maxItems, array)
+{
+    if (!array)
+        array = [];
+    if (array.length == maxDepth) {
+        let result = `vec${array.length}<T> operator.${array.join("")}<T>(vec${maxItems}<T> v)
+{
+    vec${array.length}<T> result;
 `;
+        for (let i = 0; i < array.length; ++i) {
+            result += `    result.${intToString(i)} = v.${array[i]};
+`;
+        }
+        result += `    return result;
+}
+`;
+        return result;
+    }
+    let result = "";
+    for (let i = 0; i < maxItems; ++i) {
+        array.push(intToString(i));
+        result += _generateSwizzle(maxDepth, maxItems, array);
+        array.pop();
+    }
+    return result;
+}
+
+for (let maxDepth = 2; maxDepth <= 4; maxDepth++) {
+    for (let maxItems = 2; maxItems <= 4; maxItems++)
+        standardLibrary += _generateSwizzle(maxDepth, maxItems);
+}

Added: trunk/Tools/WebGPUShadingLanguageRI/SwitchCase.js (0 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/SwitchCase.js	                        (rev 0)
+++ trunk/Tools/WebGPUShadingLanguageRI/SwitchCase.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class SwitchCase extends Node {
+    // Null value means default.
+    constructor(origin, value, body)
+    {
+        super();
+        this._origin = origin;
+        this._value = value;
+        this._body = body;
+    }
+    
+    get origin() { return this._origin; }
+    get isDefault() { return !this._value; }
+    get value() { return this._value; }
+    get body() { return this._body; }
+    
+    toString()
+    {
+        let result = "";
+        if (this.isDefault)
+            result += "default";
+        else
+            result += "cast " + this.value;
+        return result + ": " + this.body;
+    }
+}
+

Added: trunk/Tools/WebGPUShadingLanguageRI/SwitchStatement.js (0 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/SwitchStatement.js	                        (rev 0)
+++ trunk/Tools/WebGPUShadingLanguageRI/SwitchStatement.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+"use strict";
+
+class SwitchStatement extends Node {
+    constructor(origin, value)
+    {
+        super();
+        this._origin = origin;
+        this._value = value;
+        this._switchCases = [];
+    }
+    
+    get origin() { return this._origin; }
+    get value() { return this._value; }
+    
+    add(switchCase)
+    {
+        this._switchCases.push(switchCase);
+    }
+    
+    get switchCases() { return this._switchCases; }
+    
+    toString()
+    {
+        let result = "switch (" + this.value + ") { ";
+        if (this.switchCases.length)
+            result += this.switchCases.join("; "); + "; ";
+        return result + "}";
+    }
+}
+

Modified: trunk/Tools/WebGPUShadingLanguageRI/Test.html (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Test.html	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Test.html	2017-09-21 17:47:40 UTC (rev 222330)
@@ -122,6 +122,8 @@
 <script src=""
 <script src=""
 <script src=""
+<script src=""
+<script src=""
 <script src=""
 <script src=""
 <script src=""

Modified: trunk/Tools/WebGPUShadingLanguageRI/Test.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Test.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Test.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -71,6 +71,11 @@
     return TypedValue.box(program.intrinsics.uint32, value);
 }
 
+function makeUint8(program, value)
+{
+    return TypedValue.box(program.intrinsics.uint8, value);
+}
+
 function makeBool(program, value)
 {
     return TypedValue.box(program.intrinsics.bool, value);
@@ -86,6 +91,17 @@
     return TypedValue.box(program.intrinsics.double, value);
 }
 
+function makeEnum(program, enumName, value)
+{
+    let enumType = program.types.get(enumName);
+    if (!enumType)
+        throw new Error("No type named " + enumName);
+    let enumMember = enumType.memberByName(value);
+    if (!enumMember)
+        throw new Error("No member named " + enumMember + " in " + enumType);
+    return TypedValue.box(enumType, enumMember.value.unifyNode.valueForSelectedType);
+}
+
 function checkNumber(program, result, expected)
 {
     if (!result.type.unifyNode.isNumber)
@@ -117,6 +133,14 @@
         throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
 }
 
+function checkUint8(program, result, expected)
+{
+    if (!result.type.equals(program.intrinsics.uint8))
+        throw new Error("Wrong result type: " + result.type);
+    if (result.value != expected)
+        throw new Error("Wrong result: " + result.value + " (expected " + expected + ")");
+}
+
 function checkBool(program, result, expected)
 {
     if (!result.type.equals(program.intrinsics.bool))
@@ -202,10 +226,25 @@
     checkUint(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 2)]), 3);
 }
 
+function TEST_uint8SimpleMath() {
+    let program = doPrep("uint8 foo(uint8 x, uint8 y) { return x + y; }");
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), 12);
+    program = doPrep("uint8 foo(uint8 x, uint8 y) { return x - y; }");
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), 2);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 5), makeUint8(program, 7)]), 254);
+    program = doPrep("uint8 foo(uint8 x, uint8 y) { return x * y; }");
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), 35);
+    program = doPrep("uint8 foo(uint8 x, uint8 y) { return x / y; }");
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 2)]), 3);
+}
+
 function TEST_equality() {
     let program = doPrep("bool foo(uint x, uint y) { return x == y; }");
     checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), false);
     checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 7)]), true);
+    program = doPrep("bool foo(uint8 x, uint8 y) { return x == y; }");
+    checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), false);
+    checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 7)]), true);
     program = doPrep("bool foo(int x, int y) { return x == y; }");
     checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), false);
     checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 7)]), true);
@@ -227,6 +266,9 @@
     let program = doPrep("bool foo(uint x, uint y) { return x != y; }");
     checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 5)]), true);
     checkBool(program, callFunction(program, "foo", [], [makeUint(program, 7), makeUint(program, 7)]), false);
+    program = doPrep("bool foo(uint8 x, uint8 y) { return x != y; }");
+    checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 5)]), true);
+    checkBool(program, callFunction(program, "foo", [], [makeUint8(program, 7), makeUint8(program, 7)]), false);
     program = doPrep("bool foo(int x, int y) { return x != y; }");
     checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 5)]), true);
     checkBool(program, callFunction(program, "foo", [], [makeInt(program, 7), makeInt(program, 7)]), false);
@@ -3435,6 +3477,90 @@
     checkUint(program, callFunction(program, "foo", [], [makeUint(program, 0), makeUint(program, 3)]), 0);
 }
 
+function TEST_uint8BitAnd()
+{
+    let program = doPrep(`
+        uint8 foo(uint8 a, uint8 b)
+        {
+            return a & b;
+        }
+    `);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint8(program, 7)]), 1);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint8(program, 42)]), 42);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint8(program, -7)]), 249);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint8(program, 85732)]), 0);
+}
+
+function TEST_uint8BitOr()
+{
+    let program = doPrep(`
+        uint8 foo(uint8 a, uint8 b)
+        {
+            return a | b;
+        }
+    `);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint8(program, 7)]), 7);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint8(program, 42)]), 255);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint8(program, -7)]), 255);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint8(program, 85732)]), 228);
+}
+
+function TEST_uint8BitXor()
+{
+    let program = doPrep(`
+        uint8 foo(uint8 a, uint8 b)
+        {
+            return a ^ b;
+        }
+    `);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint8(program, 7)]), 6);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint8(program, 42)]), 213);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint8(program, -7)]), 6);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint8(program, 85732)]), 228);
+}
+
+function TEST_uint8BitNot()
+{
+    let program = doPrep(`
+        uint8 foo(uint8 a)
+        {
+            return ~a;
+        }
+    `);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1)]), 254);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535)]), 0);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1)]), 0);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0)]), 255);
+}
+
+function TEST_uint8LShift()
+{
+    let program = doPrep(`
+        uint8 foo(uint8 a, uint b)
+        {
+            return a << b;
+        }
+    `);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint(program, 7)]), 128);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint(program, 2)]), 252);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint(program, 5)]), 224);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint(program, 3)]), 0);
+}
+
+function TEST_uint8RShift()
+{
+    let program = doPrep(`
+        uint8 foo(uint8 a, uint b)
+        {
+            return a >> b;
+        }
+    `);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 1), makeUint(program, 7)]), 0);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 65535), makeUint(program, 2)]), 255);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, -1), makeUint(program, 5)]), 255);
+    checkUint8(program, callFunction(program, "foo", [], [makeUint8(program, 0), makeUint(program, 3)]), 0);
+}
+
 function TEST_floatMath()
 {
     let program = doPrep(`
@@ -4389,6 +4515,577 @@
     checkFloat(program, callFunction(program, "foo3", [], []), 4);
 }
 
+function TEST_enumWithExplicitIntBase()
+{
+    let program = doPrep(`
+        enum Foo : int {
+            War,
+            Famine,
+            Pestilence,
+            Death
+        }
+        Foo war()
+        {
+            return Foo.War;
+        }
+        Foo famine()
+        {
+            return Foo.Famine;
+        }
+        Foo pestilence()
+        {
+            return Foo.Pestilence;
+        }
+        Foo death()
+        {
+            return Foo.Death;
+        }
+        bool equals(Foo a, Foo b)
+        {
+            return a == b;
+        }
+        bool notEquals(Foo a, Foo b)
+        {
+            return a != b;
+        }
+        bool testSimpleEqual()
+        {
+            return equals(Foo.War, Foo.War);
+        }
+        bool testAnotherEqual()
+        {
+            return equals(Foo.Pestilence, Foo.Pestilence);
+        }
+        bool testNotEqual()
+        {
+            return equals(Foo.Famine, Foo.Death);
+        }
+        bool testSimpleNotEqual()
+        {
+            return notEquals(Foo.War, Foo.War);
+        }
+        bool testAnotherNotEqual()
+        {
+            return notEquals(Foo.Pestilence, Foo.Pestilence);
+        }
+        bool testNotNotEqual()
+        {
+            return notEquals(Foo.Famine, Foo.Death);
+        }
+        int intWar()
+        {
+            return int(war());
+        }
+        int intFamine()
+        {
+            return int(famine());
+        }
+        int intPestilence()
+        {
+            return int(pestilence());
+        }
+        int intDeath()
+        {
+            return int(death());
+        }
+        int warValue()
+        {
+            return war().value;
+        }
+        int famineValue()
+        {
+            return famine().value;
+        }
+        int pestilenceValue()
+        {
+            return pestilence().value;
+        }
+        int deathValue()
+        {
+            return death().value;
+        }
+        int warValueLiteral()
+        {
+            return Foo.War.value;
+        }
+        int famineValueLiteral()
+        {
+            return Foo.Famine.value;
+        }
+        int pestilenceValueLiteral()
+        {
+            return Foo.Pestilence.value;
+        }
+        int deathValueLiteral()
+        {
+            return Foo.Death.value;
+        }
+        Foo intWarBackwards()
+        {
+            return Foo(intWar());
+        }
+        Foo intFamineBackwards()
+        {
+            return Foo(intFamine());
+        }
+        Foo intPestilenceBackwards()
+        {
+            return Foo(intPestilence());
+        }
+        Foo intDeathBackwards()
+        {
+            return Foo(intDeath());
+        }
+    `);
+    checkEnum(program, callFunction(program, "war", [], []), 0);
+    checkEnum(program, callFunction(program, "famine", [], []), 1);
+    checkEnum(program, callFunction(program, "pestilence", [], []), 2);
+    checkEnum(program, callFunction(program, "death", [], []), 3);
+    checkBool(program, callFunction(program, "testSimpleEqual", [], []), true);
+    checkBool(program, callFunction(program, "testAnotherEqual", [], []), true);
+    checkBool(program, callFunction(program, "testNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testSimpleNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testAnotherNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testNotNotEqual", [], []), true);
+    checkInt(program, callFunction(program, "intWar", [], []), 0);
+    checkInt(program, callFunction(program, "intFamine", [], []), 1);
+    checkInt(program, callFunction(program, "intPestilence", [], []), 2);
+    checkInt(program, callFunction(program, "intDeath", [], []), 3);
+    checkInt(program, callFunction(program, "warValue", [], []), 0);
+    checkInt(program, callFunction(program, "famineValue", [], []), 1);
+    checkInt(program, callFunction(program, "pestilenceValue", [], []), 2);
+    checkInt(program, callFunction(program, "deathValue", [], []), 3);
+    checkInt(program, callFunction(program, "warValueLiteral", [], []), 0);
+    checkInt(program, callFunction(program, "famineValueLiteral", [], []), 1);
+    checkInt(program, callFunction(program, "pestilenceValueLiteral", [], []), 2);
+    checkInt(program, callFunction(program, "deathValueLiteral", [], []), 3);
+    checkEnum(program, callFunction(program, "intWarBackwards", [], []), 0);
+    checkEnum(program, callFunction(program, "intFamineBackwards", [], []), 1);
+    checkEnum(program, callFunction(program, "intPestilenceBackwards", [], []), 2);
+    checkEnum(program, callFunction(program, "intDeathBackwards", [], []), 3);
+}
+
+function TEST_enumWithUintBase()
+{
+    let program = doPrep(`
+        enum Foo : uint {
+            War,
+            Famine,
+            Pestilence,
+            Death
+        }
+        Foo war()
+        {
+            return Foo.War;
+        }
+        Foo famine()
+        {
+            return Foo.Famine;
+        }
+        Foo pestilence()
+        {
+            return Foo.Pestilence;
+        }
+        Foo death()
+        {
+            return Foo.Death;
+        }
+        bool equals(Foo a, Foo b)
+        {
+            return a == b;
+        }
+        bool notEquals(Foo a, Foo b)
+        {
+            return a != b;
+        }
+        bool testSimpleEqual()
+        {
+            return equals(Foo.War, Foo.War);
+        }
+        bool testAnotherEqual()
+        {
+            return equals(Foo.Pestilence, Foo.Pestilence);
+        }
+        bool testNotEqual()
+        {
+            return equals(Foo.Famine, Foo.Death);
+        }
+        bool testSimpleNotEqual()
+        {
+            return notEquals(Foo.War, Foo.War);
+        }
+        bool testAnotherNotEqual()
+        {
+            return notEquals(Foo.Pestilence, Foo.Pestilence);
+        }
+        bool testNotNotEqual()
+        {
+            return notEquals(Foo.Famine, Foo.Death);
+        }
+        uint uintWar()
+        {
+            return uint(war());
+        }
+        uint uintFamine()
+        {
+            return uint(famine());
+        }
+        uint uintPestilence()
+        {
+            return uint(pestilence());
+        }
+        uint uintDeath()
+        {
+            return uint(death());
+        }
+        uint warValue()
+        {
+            return war().value;
+        }
+        uint famineValue()
+        {
+            return famine().value;
+        }
+        uint pestilenceValue()
+        {
+            return pestilence().value;
+        }
+        uint deathValue()
+        {
+            return death().value;
+        }
+        uint warValueLiteral()
+        {
+            return Foo.War.value;
+        }
+        uint famineValueLiteral()
+        {
+            return Foo.Famine.value;
+        }
+        uint pestilenceValueLiteral()
+        {
+            return Foo.Pestilence.value;
+        }
+        uint deathValueLiteral()
+        {
+            return Foo.Death.value;
+        }
+        Foo uintWarBackwards()
+        {
+            return Foo(uintWar());
+        }
+        Foo uintFamineBackwards()
+        {
+            return Foo(uintFamine());
+        }
+        Foo uintPestilenceBackwards()
+        {
+            return Foo(uintPestilence());
+        }
+        Foo uintDeathBackwards()
+        {
+            return Foo(uintDeath());
+        }
+    `);
+    checkEnum(program, callFunction(program, "war", [], []), 0);
+    checkEnum(program, callFunction(program, "famine", [], []), 1);
+    checkEnum(program, callFunction(program, "pestilence", [], []), 2);
+    checkEnum(program, callFunction(program, "death", [], []), 3);
+    checkBool(program, callFunction(program, "testSimpleEqual", [], []), true);
+    checkBool(program, callFunction(program, "testAnotherEqual", [], []), true);
+    checkBool(program, callFunction(program, "testNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testSimpleNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testAnotherNotEqual", [], []), false);
+    checkBool(program, callFunction(program, "testNotNotEqual", [], []), true);
+    checkUint(program, callFunction(program, "uintWar", [], []), 0);
+    checkUint(program, callFunction(program, "uintFamine", [], []), 1);
+    checkUint(program, callFunction(program, "uintPestilence", [], []), 2);
+    checkUint(program, callFunction(program, "uintDeath", [], []), 3);
+    checkUint(program, callFunction(program, "warValue", [], []), 0);
+    checkUint(program, callFunction(program, "famineValue", [], []), 1);
+    checkUint(program, callFunction(program, "pestilenceValue", [], []), 2);
+    checkUint(program, callFunction(program, "deathValue", [], []), 3);
+    checkUint(program, callFunction(program, "warValueLiteral", [], []), 0);
+    checkUint(program, callFunction(program, "famineValueLiteral", [], []), 1);
+    checkUint(program, callFunction(program, "pestilenceValueLiteral", [], []), 2);
+    checkUint(program, callFunction(program, "deathValueLiteral", [], []), 3);
+    checkEnum(program, callFunction(program, "uintWarBackwards", [], []), 0);
+    checkEnum(program, callFunction(program, "uintFamineBackwards", [], []), 1);
+    checkEnum(program, callFunction(program, "uintPestilenceBackwards", [], []), 2);
+    checkEnum(program, callFunction(program, "uintDeathBackwards", [], []), 3);
+}
+
+function TEST_enumFloatBase()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo : float {
+                Bar
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_enumPtrBase()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo : thread int^ {
+                Bar
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_enumArrayRefBase()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo : thread int[] {
+                Bar
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_emptyStruct()
+{
+    let program = doPrep(`
+        struct Thingy { }
+        int foo()
+        {
+            Thingy thingy;
+            return 46;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], []), 46);
+}
+
+function TEST_enumStructBase()
+{
+    checkFail(
+        () => doPrep(`
+            struct Thingy { }
+            enum Foo : Thingy {
+                Bar
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_enumNoMembers()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo { }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_simpleSwitch()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            switch (x) {
+            case 767:
+                return 27;
+            case 69:
+                return 7624;
+            default:
+                return 49;
+            }
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
+}
+
+function TEST_simpleSwitch()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            switch (x) {
+            case 767:
+                return 27;
+            case 69:
+                return 7624;
+            default:
+                return 49;
+            }
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
+}
+
+function TEST_exhaustiveUint8Switch()
+{
+    let text = "double foo(uint8 x) { switch (uint8(x)) {"
+    for (let i = 0; i <= 0xff; ++i)
+        text += "case " + i + ": return " + i * 1.5 + ";";
+    text += "} }";
+    let program = doPrep(text);
+    for (let i = 0; i < 0xff; ++i)
+        checkDouble(program, callFunction(program, "foo", [], [makeUint8(program, i)]), i * 1.5);
+}
+
+function TEST_notQuiteExhaustiveUint8Switch()
+{
+    let text = "double foo(uint8 x) { switch (uint8(x)) {"
+    for (let i = 0; i <= 0xfe; ++i)
+        text += "case " + i + ": return " + i * 1.5 + ";";
+    text += "} }";
+    checkFail(() => doPrep(text), e => e instanceof WTypeError);
+}
+
+function TEST_notQuiteExhaustiveUint8SwitchWithDefault()
+{
+    let text = "double foo(uint8 x) { switch (uint8(x)) {"
+    for (let i = 0; i <= 0xfe; ++i)
+        text += "case " + i + ": return " + i * 1.5 + ";";
+    text += "default: return " + 0xff * 1.5 + ";";
+    text += "} }";
+    let program = doPrep(text);
+    for (let i = 0; i < 0xff; ++i)
+        checkDouble(program, callFunction(program, "foo", [], [makeUint8(program, i)]), i * 1.5);
+}
+
+function TEST_switchFallThrough()
+{
+    // FIXME: This might become an error in future versions.
+    // https://bugs.webkit.org/show_bug.cgi?id=177172
+    let program = doPrep(`
+        int foo(int x)
+        {
+            int result = 0;
+            switch (x) {
+            case 767:
+                result += 27;
+            case 69:
+                result += 7624;
+            default:
+                result += 49;
+            }
+            return result;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27 + 7624 + 49);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624 + 49);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
+}
+
+function TEST_switchBreak()
+{
+    let program = doPrep(`
+        int foo(int x)
+        {
+            int result = 0;
+            switch (x) {
+            case 767:
+                result += 27;
+                break;
+            case 69:
+                result += 7624;
+                break;
+            default:
+                result += 49;
+                break;
+            }
+            return result;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 767)]), 27);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 69)]), 7624);
+    checkInt(program, callFunction(program, "foo", [], [makeInt(program, 0)]), 49);
+}
+
+function TEST_enumSwitchBreakExhaustive()
+{
+    let program = doPrep(`
+        enum Foo {
+            A, B, C
+        }
+        int foo(Foo x)
+        {
+            int result = 0;
+            switch (x) {
+            case Foo.A:
+                result += 27;
+                break;
+            case Foo.B:
+                result += 7624;
+                break;
+            case Foo.C:
+                result += 49;
+                break;
+            }
+            return result;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "A")]), 27);
+    checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "B")]), 7624);
+    checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "C")]), 49);
+}
+
+function TEST_enumSwitchBreakNotQuiteExhaustive()
+{
+    checkFail(
+        () => doPrep(`
+            enum Foo {
+                A, B, C, D
+            }
+            int foo(Foo x)
+            {
+                int result = 0;
+                switch (x) {
+                case Foo.A:
+                    result += 27;
+                    break;
+                case Foo.B:
+                    result += 7624;
+                    break;
+                case Foo.C:
+                    result += 49;
+                    break;
+                }
+                return result;
+            }
+        `),
+        e => e instanceof WTypeError);
+}
+
+function TEST_enumSwitchBreakNotQuiteExhaustiveWithDefault()
+{
+    let program = doPrep(`
+        enum Foo {
+            A, B, C
+        }
+        int foo(Foo x)
+        {
+            int result = 0;
+            switch (x) {
+            case Foo.A:
+                result += 27;
+                break;
+            case Foo.B:
+                result += 7624;
+                break;
+            default:
+                result += 49;
+                break;
+            }
+            return result;
+        }
+    `);
+    checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "A")]), 27);
+    checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "B")]), 7624);
+    checkInt(program, callFunction(program, "foo", [], [makeEnum(program, "Foo", "C")]), 49);
+}
+
 function TEST_simpleRecursiveStruct()
 {
     checkFail(
@@ -4507,7 +5204,11 @@
     prepare();
     print("    OK!");
 
-    for (let s in object) {
+    let names = [];
+    for (let s in object)
+        names.push(s);
+    names.sort();
+    for (let s of names) {
         if (s.startsWith("TEST_") && s.match(filter)) {
             print(s + "...");
             yield;

Modified: trunk/Tools/WebGPUShadingLanguageRI/UnificationContext.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/UnificationContext.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/UnificationContext.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -29,6 +29,7 @@
     {
         this._typeParameters = new Set(typeParameters);
         this._nextMap = new Map();
+        this._extraNodes = new Set();
     }
     
     union(a, b)
@@ -67,6 +68,11 @@
         return currentNode;
     }
     
+    addExtraNode(node)
+    {
+        this._extraNodes.add(node);
+    }
+    
     get nodes()
     {
         let result = new Set();
@@ -74,6 +80,8 @@
             result.add(key);
             result.add(value);
         }
+        for (let node of this._extraNodes)
+            result.add(node);
         return result;
     }
     

Modified: trunk/Tools/WebGPUShadingLanguageRI/Visitor.js (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/Visitor.js	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/Visitor.js	2017-09-21 17:47:40 UTC (rev 222330)
@@ -278,6 +278,19 @@
         Node.visit(node.increment, this);
         node.body.visit(this);
     }
+    
+    visitSwitchStatement(node)
+    {
+        node.value.visit(this);
+        for (let switchCase of node.switchCases)
+            switchCase.visit(this);
+    }
+    
+    visitSwitchCase(node)
+    {
+        Node.visit(node.value, this);
+        node.body.visit(this);
+    }
 
     visitReturn(node)
     {

Modified: trunk/Tools/WebGPUShadingLanguageRI/index.html (222329 => 222330)


--- trunk/Tools/WebGPUShadingLanguageRI/index.html	2017-09-21 17:43:07 UTC (rev 222329)
+++ trunk/Tools/WebGPUShadingLanguageRI/index.html	2017-09-21 17:47:40 UTC (rev 222330)
@@ -122,6 +122,8 @@
 <script src=""
 <script src=""
 <script src=""
+<script src=""
+<script src=""
 <script src=""
 <script src=""
 <script src=""
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to