Turned on SkSL->GLSL compiler
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2288033003

Committed: https://skia.googlesource.com/skia/+/9b0fe3d125f237d9884732a48414fa85fc71b4e3
Review-Url: https://codereview.chromium.org/2288033003
diff --git a/gyp/sksl.gyp b/gyp/sksl.gyp
index 609ace9..f45b702 100644
--- a/gyp/sksl.gyp
+++ b/gyp/sksl.gyp
@@ -18,7 +18,7 @@
       ],
       'all_dependent_settings': {
         'include_dirs': [
-          '../src/sksl',
+          '<(skia_src_path)/sksl',
         ],
       },
     },
diff --git a/src/gpu/gl/GrGLContext.cpp b/src/gpu/gl/GrGLContext.cpp
index 9e70b47..2126314 100644
--- a/src/gpu/gl/GrGLContext.cpp
+++ b/src/gpu/gl/GrGLContext.cpp
@@ -7,6 +7,7 @@
 
 #include "GrGLContext.h"
 #include "GrGLGLSL.h"
+#include "SkSLCompiler.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -63,6 +64,17 @@
     return new GrGLContext(args);
 }
 
+GrGLContext::~GrGLContext() {
+    delete fCompiler;
+}
+
+SkSL::Compiler* GrGLContext::compiler() const {
+    if (!fCompiler) {
+        fCompiler = new SkSL::Compiler();
+    }
+    return fCompiler;
+}
+
 GrGLContextInfo::GrGLContextInfo(const ConstructorArgs& args) {
     fInterface.reset(SkRef(args.fInterface));
     fGLVersion = args.fGLVersion;
diff --git a/src/gpu/gl/GrGLContext.h b/src/gpu/gl/GrGLContext.h
index 6016f68..8207ac8 100644
--- a/src/gpu/gl/GrGLContext.h
+++ b/src/gpu/gl/GrGLContext.h
@@ -15,6 +15,9 @@
 #include "GrGLUtil.h"
 
 struct GrContextOptions;
+namespace SkSL {
+    class Compiler;
+}
 
 /**
  * Encapsulates information about an OpenGL context including the OpenGL
@@ -39,6 +42,8 @@
 
     const GrGLExtensions& extensions() const { return fInterface->fExtensions; }
 
+    virtual ~GrGLContextInfo() {}
+
 protected:
     struct ConstructorArgs {
         const GrGLInterface*                fInterface;
@@ -64,7 +69,7 @@
 };
 
 /**
- * Extension of GrGLContextInfo that also provides access to GrGLInterface.
+ * Extension of GrGLContextInfo that also provides access to GrGLInterface and SkSL::Compiler.
  */
 class GrGLContext : public GrGLContextInfo {
 public:
@@ -76,8 +81,16 @@
 
     const GrGLInterface* interface() const { return fInterface; }
 
+    SkSL::Compiler* compiler() const;
+
+    ~GrGLContext() override;
+
 private:
-    GrGLContext(const ConstructorArgs& args) : INHERITED(args) {}
+    GrGLContext(const ConstructorArgs& args) 
+    : INHERITED(args)
+    , fCompiler(nullptr) {}
+
+    mutable SkSL::Compiler* fCompiler;
 
     typedef GrGLContextInfo INHERITED;
 };
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index fcd3270..341216a 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -3790,20 +3790,11 @@
     fshaderTxt.append(";");
     uTexture.appendDecl(glslCaps, &fshaderTxt);
     fshaderTxt.append(";");
-    const char* fsOutName;
-    if (glslCaps->mustDeclareFragmentShaderOutput()) {
-        oFragColor.appendDecl(glslCaps, &fshaderTxt);
-        fshaderTxt.append(";");
-        fsOutName = oFragColor.c_str();
-    } else {
-        fsOutName = "gl_FragColor";
-    }
     fshaderTxt.appendf(
         "// Copy Program FS\n"
         "void main() {"
-        "  %s = %s(u_texture, v_texCoord);"
+        "  sk_FragColor = %s(u_texture, v_texCoord);"
         "}",
-        fsOutName,
         GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kSamplerTypes[progIdx], this->glslGeneration())
     );
 
@@ -3936,14 +3927,6 @@
     }
     uTexture.appendDecl(glslCaps, &fshaderTxt);
     fshaderTxt.append(";");
-    const char* fsOutName;
-    if (glslCaps->mustDeclareFragmentShaderOutput()) {
-        oFragColor.appendDecl(glslCaps, &fshaderTxt);
-        fshaderTxt.append(";");
-        fsOutName = oFragColor.c_str();
-    } else {
-        fsOutName = "gl_FragColor";
-    }
     const char* sampleFunction = GrGLSLTexture2DFunctionName(kVec2f_GrSLType,
                                                              kTexture2DSampler_GrSLType,
                                                              this->glslGeneration());
@@ -3954,19 +3937,19 @@
 
     if (oddWidth && oddHeight) {
         fshaderTxt.appendf(
-            "  %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + "
-            "        %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;",
-            fsOutName, sampleFunction, sampleFunction, sampleFunction, sampleFunction
+            "  sk_FragColor = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + "
+            "                  %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;",
+            sampleFunction, sampleFunction, sampleFunction, sampleFunction
         );
     } else if (oddWidth || oddHeight) {
         fshaderTxt.appendf(
-            "  %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;",
-            fsOutName, sampleFunction, sampleFunction
+            "  sk_FragColor = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;",
+            sampleFunction, sampleFunction
         );
     } else {
         fshaderTxt.appendf(
-            "  %s = %s(u_texture, v_texCoord0);",
-            fsOutName, sampleFunction
+            "  sk_FragColor = %s(u_texture, v_texCoord0);",
+            sampleFunction
         );
     }
 
@@ -4053,20 +4036,11 @@
                                                  &fshaderTxt);
     uColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
     fshaderTxt.append(";");
-    const char* fsOutName;
-    if (this->glCaps().glslCaps()->mustDeclareFragmentShaderOutput()) {
-        oFragColor.appendDecl(this->glCaps().glslCaps(), &fshaderTxt);
-        fshaderTxt.append(";");
-        fsOutName = oFragColor.c_str();
-    } else {
-        fsOutName = "gl_FragColor";
-    }
     fshaderTxt.appendf(
         "// Write Rect Program FS\n"
         "void main() {"
-        "  %s = %s;"
+        "  sk_FragColor = %s;"
         "}",
-        fsOutName,
         uColor.c_str()
     );
 
diff --git a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
index d2e49a5..04d90ee 100644
--- a/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
+++ b/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
@@ -9,6 +9,8 @@
 #include "gl/GrGLGpu.h"
 #include "gl/GrGLSLPrettyPrint.h"
 #include "SkTraceEvent.h"
+#include "SkSLCompiler.h"
+#include "ir/SkSLProgram.h"
 
 #define GL_CALL(X) GR_GL_CALL(gpu->glInterface(), X)
 #define GL_CALL_RET(R, X) GR_GL_CALL_RET(gpu->glInterface(), R, X)
@@ -18,6 +20,90 @@
 
 static void print_shader_source(const char** strings, int* lengths, int count);
 
+static SkSL::GLCaps skslcaps_for_context(const GrGLContext& context) {
+    GrGLStandard standard = context.standard();
+    const GrGLCaps* caps = context.caps();
+    const GrGLSLCaps* glslCaps = caps->glslCaps();
+    SkSL::GLCaps result;
+    switch (standard) {
+        case kGL_GrGLStandard:
+            result.fStandard = SkSL::GLCaps::kGL_Standard;
+            break;
+        case kGLES_GrGLStandard:
+            result.fStandard = SkSL::GLCaps::kGLES_Standard;
+            break;
+        default:
+            SkASSERT(false);
+            result.fStandard = SkSL::GLCaps::kGL_Standard;
+    }
+
+    switch (glslCaps->generation()) {
+        case k110_GrGLSLGeneration:
+            if (kGLES_GrGLStandard == standard) {
+                // ES2's shader language is based on GLSL 1.20 but is version 1.00 of the ES 
+                // language
+                result.fVersion = 100;
+            } else {
+                SkASSERT(kGL_GrGLStandard == standard);
+                result.fVersion = 110;
+            }
+            break;
+        case k130_GrGLSLGeneration:
+            SkASSERT(kGL_GrGLStandard == standard);
+            result.fVersion = 130;
+            break;
+        case k140_GrGLSLGeneration:
+            SkASSERT(kGL_GrGLStandard == standard);
+            result.fVersion = 140;
+            break;
+        case k150_GrGLSLGeneration:
+            SkASSERT(kGL_GrGLStandard == standard);
+            result.fVersion = 150;
+            break;
+        case k330_GrGLSLGeneration:
+            if (kGLES_GrGLStandard == standard) {
+                result.fVersion = 300;
+            } else {
+                SkASSERT(kGL_GrGLStandard == standard);
+                result.fVersion = 330;
+            }
+            break;
+        case k400_GrGLSLGeneration:
+            SkASSERT(kGL_GrGLStandard == standard);
+            result.fVersion = 400;
+            break;
+        case k310es_GrGLSLGeneration:
+            SkASSERT(kGLES_GrGLStandard == standard);
+            result.fVersion = 310;
+            break;
+        case k320es_GrGLSLGeneration:
+            SkASSERT(kGLES_GrGLStandard == standard);
+            result.fVersion = 320;
+            break;
+    }
+    result.fIsCoreProfile = caps->isCoreProfile();
+    result.fUsesPrecisionModifiers = glslCaps->usesPrecisionModifiers();
+    result.fMustDeclareFragmentShaderOutput = glslCaps->mustDeclareFragmentShaderOutput();
+    result.fCanUseMinAndAbsTogether = glslCaps->canUseMinAndAbsTogether();
+    return result;
+}
+
+static void dump_string(std::string s) {
+    // on Android, SkDebugf only displays the first 1K characters of output, which results in
+    // incomplete shader source code. Print each line individually to avoid this problem.
+    size_t index = 0;
+    for (;;) {
+        size_t next = s.find("\n", index);
+        if (next == std::string::npos) {
+            SkDebugf("%s", s.substr(index).c_str());
+            break;
+        } else {
+            SkDebugf("%s", s.substr(index, next - index + 1).c_str());
+            index = next + 1;
+        }
+    }
+}
+
 GrGLuint GrGLCompileAndAttachShader(const GrGLContext& glCtx,
                                     GrGLuint programId,
                                     GrGLenum type,
@@ -33,15 +119,39 @@
         return 0;
     }
 
+    std::string sksl;
 #ifdef SK_DEBUG
     SkString prettySource = GrGLSLPrettyPrint::PrettyPrintGLSL(strings, lengths, count, false);
-    const GrGLchar* sourceStr = prettySource.c_str();
-    GrGLint sourceLength = static_cast<GrGLint>(prettySource.size());
-    GR_GL_CALL(gli, ShaderSource(shaderId, 1, &sourceStr, &sourceLength));
+    sksl = std::string(prettySource.c_str());
 #else
-    GR_GL_CALL(gli, ShaderSource(shaderId, count, strings, lengths));
+    for (int i = 0; i < count; i++) {
+        sksl.append(strings[i], lengths[i]);
+    }
 #endif
 
+    std::string glsl;
+    SkSL::Compiler& compiler = *glCtx.compiler();
+    SkSL::GLCaps caps = skslcaps_for_context(glCtx);
+    SkASSERT(type == GR_GL_VERTEX_SHADER || type == GR_GL_FRAGMENT_SHADER);
+    SkDEBUGCODE(bool result = )compiler.toGLSL(type == GR_GL_VERTEX_SHADER 
+                                                                    ? SkSL::Program::kVertex_Kind
+                                                                    : SkSL::Program::kFragment_Kind,
+                                               std::string(sksl.c_str()),
+                                               caps,
+                                               &glsl);
+#ifdef SK_DEBUG
+    if (!result) {
+        SkDebugf("SKSL compilation error\n----------------------\n");
+        SkDebugf("SKSL:\n");
+        dump_string(sksl);
+        SkDebugf("\nErrors:\n%s\n", compiler.errorText().c_str());
+        SkDEBUGFAIL("SKSL compilation failed!\n");
+    }
+#endif
+    const char* glslChars = glsl.c_str();
+    GrGLint glslLength = (GrGLint) glsl.length();
+    GR_GL_CALL(gli, ShaderSource(shaderId, 1, &glslChars, &glslLength));
+
     // If tracing is enabled in chrome then we pretty print
     bool traceShader;
     TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("skia.gpu"), &traceShader);
@@ -72,10 +182,14 @@
                 // buffer param validation.
                 GrGLsizei length = GR_GL_INIT_ZERO;
                 GR_GL_CALL(gli, GetShaderInfoLog(shaderId, infoLen+1, &length, (char*)log.get()));
-                print_shader_source(strings, lengths, count);
-                SkDebugf("\n%s", (const char*)log.get());
+                SkDebugf("GLSL compilation error\n----------------------\n");
+                SkDebugf("SKSL:\n");
+                dump_string(sksl);
+                SkDebugf("GLSL:\n");
+                dump_string(glsl);
+                SkDebugf("Errors:\n%s\n", (const char*) log.get());
             }
-            SkDEBUGFAIL("Shader compilation failed!");
+            SkDEBUGFAIL("GLSL compilation failed!");
             GR_GL_CALL(gli, DeleteShader(shaderId));
             return 0;
         }
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
index d35730f..eb744a9 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.cpp
@@ -291,10 +291,10 @@
     if (!fHasCustomColorOutput) {
         fHasCustomColorOutput = true;
         fCustomColorOutputIndex = fOutputs.count();
-        fOutputs.push_back().set(kVec4f_GrSLType,
-                                 GrGLSLShaderVar::kOut_TypeModifier,
-                                 DeclaredColorOutputName());
-        fProgramBuilder->finalizeFragmentOutputColor(fOutputs.back());
+        fOutputs.push_back().set(kVec4f_GrSLType,  
+                                 GrGLSLShaderVar::kOut_TypeModifier,   
+                                 DeclaredColorOutputName());   
+        fProgramBuilder->finalizeFragmentOutputColor(fOutputs.back()); 
     }
 }
 
@@ -318,7 +318,7 @@
 }
 
 const char* GrGLSLFragmentShaderBuilder::getPrimaryColorOutputName() const {
-    return fHasCustomColorOutput ? DeclaredColorOutputName() : "gl_FragColor";
+    return fHasCustomColorOutput ? DeclaredColorOutputName() : "sk_FragColor";
 }
 
 void GrGLSLFragmentBuilder::declAppendf(const char* fmt, ...) {
diff --git a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
index ecb6d45..6a4a184 100644
--- a/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLFragmentShaderBuilder.h
@@ -203,7 +203,7 @@
     }
 #endif
 
-    static const char* DeclaredColorOutputName() { return "fsColorOut"; }
+    static const char* DeclaredColorOutputName() { return "sk_FragColor"; }
     static const char* DeclaredSecondaryColorOutputName() { return "fsSecondaryColorOut"; }
 
     GrSurfaceOrigin getSurfaceOrigin() const;
diff --git a/src/gpu/glsl/GrGLSLShaderBuilder.h b/src/gpu/glsl/GrGLSLShaderBuilder.h
index a6ff96d..f59edb4 100644
--- a/src/gpu/glsl/GrGLSLShaderBuilder.h
+++ b/src/gpu/glsl/GrGLSLShaderBuilder.h
@@ -72,23 +72,27 @@
     void appendTexelFetch(SamplerHandle, const char* coordExpr);
 
     /**
-    * Adds a #define directive to the top of the shader.
+    * Adds a constant declaration to the top of the shader.
     */
-    void define(const char* macro, const char* replacement) {
-        this->definitions().appendf("#define %s %s\n", macro, replacement);
+    void defineConstant(const char* type, const char* name, const char* value) {
+        this->definitions().appendf("const %s %s = %s;\n", type, name, value);
     }
 
-    void define(const char* macro, int replacement) {
-        this->definitions().appendf("#define %s %i\n", macro, replacement);
+    void defineConstant(const char* name, int value) {
+        this->definitions().appendf("const int %s = %i;\n", name, value);
     }
 
-    void definef(const char* macro, const char* replacement, ...) {
-       this->definitions().appendf("#define %s ", macro);
+    void defineConstant(const char* name, float value) {
+        this->definitions().appendf("const float %s = %f;\n", name, value);
+    }
+
+    void defineConstantf(const char* type, const char* name, const char* fmt, ...) {
+       this->definitions().appendf("const %s %s = ", type, name);
        va_list args;
-       va_start(args, replacement);
-       this->definitions().appendVAList(replacement, args);
+       va_start(args, fmt);
+       this->definitions().appendVAList(fmt, args);
        va_end(args);
-       this->definitions().append("\n");
+       this->definitions().append(";\n");
     }
 
     /**
diff --git a/src/gpu/glsl/GrGLSLShaderVar.h b/src/gpu/glsl/GrGLSLShaderVar.h
index 9d162ec..35ac4bc 100644
--- a/src/gpu/glsl/GrGLSLShaderVar.h
+++ b/src/gpu/glsl/GrGLSLShaderVar.h
@@ -213,24 +213,20 @@
 
 private:
     static const char* TypeModifierString(const GrGLSLCaps* glslCaps, TypeModifier t) {
-        GrGLSLGeneration gen = glslCaps->generation();
         switch (t) {
             case kNone_TypeModifier:
                 return "";
+            case kAttribute_TypeModifier: // fall through
+            case kVaryingIn_TypeModifier: // fall through
             case kIn_TypeModifier:
                 return "in";
             case kInOut_TypeModifier:
                 return "inout";
+            case kVaryingOut_TypeModifier: // fall through
             case kOut_TypeModifier:
                 return "out";
             case kUniform_TypeModifier:
                 return "uniform";
-            case kAttribute_TypeModifier:
-                return k110_GrGLSLGeneration == gen ? "attribute" : "in";
-            case kVaryingIn_TypeModifier:
-                return k110_GrGLSLGeneration == gen ? "varying" : "in";
-            case kVaryingOut_TypeModifier:
-                return k110_GrGLSLGeneration == gen ? "varying" : "out";
             default:
                 SkFAIL("Unknown shader variable type modifier.");
                 return ""; // suppress warning
diff --git a/src/gpu/instanced/InstanceProcessor.cpp b/src/gpu/instanced/InstanceProcessor.cpp
index 480155b..82116c4 100644
--- a/src/gpu/instanced/InstanceProcessor.cpp
+++ b/src/gpu/instanced/InstanceProcessor.cpp
@@ -107,10 +107,10 @@
 
     void initParams(const SamplerHandle paramsBuffer) {
         fParamsBuffer = paramsBuffer;
-        fVertexBuilder->definef("PARAMS_IDX_MASK", "0x%xu", kParamsIdx_InfoMask);
         fVertexBuilder->appendPrecisionModifier(kHigh_GrSLPrecision);
-        fVertexBuilder->codeAppendf("int paramsIdx = int(%s & PARAMS_IDX_MASK);",
-                                    this->attr(Attrib::kInstanceInfo));
+        fVertexBuilder->codeAppendf("int paramsIdx = int(%s & 0x%x);",
+                                    this->attr(Attrib::kInstanceInfo),
+                                    kParamsIdx_InfoMask);
     }
 
     const char* attr(Attrib attr) const { return fInstProc.getAttrib((int)attr).fName; }
@@ -224,10 +224,10 @@
         v->codeAppendf("mat2x3 shapeMatrix = mat2x3(%s, %s);",
                        inputs.attr(Attrib::kShapeMatrixX), inputs.attr(Attrib::kShapeMatrixY));
     } else {
-        v->definef("PERSPECTIVE_FLAG", "0x%xu", kPerspective_InfoFlag);
+        v->defineConstantf("int", "PERSPECTIVE_FLAG", "0x%x", kPerspective_InfoFlag);
         v->codeAppendf("mat3 shapeMatrix = mat3(%s, %s, vec3(0, 0, 1));",
                        inputs.attr(Attrib::kShapeMatrixX), inputs.attr(Attrib::kShapeMatrixY));
-        v->codeAppendf("if (0u != (%s & PERSPECTIVE_FLAG)) {",
+        v->codeAppendf("if (0 != (%s & PERSPECTIVE_FLAG)) {",
                        inputs.attr(Attrib::kInstanceInfo));
         v->codeAppend (    "shapeMatrix[2] = ");
         inputs.fetchNextParam(kVec3f_GrSLType);
@@ -237,7 +237,7 @@
 
     bool hasSingleShapeType = SkIsPow2(ip.batchInfo().fShapeTypes);
     if (!hasSingleShapeType) {
-        v->define("SHAPE_TYPE_BIT", kShapeType_InfoBit);
+        v->defineConstant("SHAPE_TYPE_BIT", kShapeType_InfoBit);
         v->codeAppendf("uint shapeType = %s >> SHAPE_TYPE_BIT;",
                        inputs.attr(Attrib::kInstanceInfo));
     }
@@ -285,8 +285,8 @@
     if (ip.batchInfo().fInnerShapeTypes) {
         bool hasSingleInnerShapeType = SkIsPow2(ip.batchInfo().fInnerShapeTypes);
         if (!hasSingleInnerShapeType) {
-            v->definef("INNER_SHAPE_TYPE_MASK", "0x%xu", kInnerShapeType_InfoMask);
-            v->define("INNER_SHAPE_TYPE_BIT", kInnerShapeType_InfoBit);
+            v->defineConstantf("int", "INNER_SHAPE_TYPE_MASK", "0x%x", kInnerShapeType_InfoMask);
+            v->defineConstant("INNER_SHAPE_TYPE_BIT", kInnerShapeType_InfoBit);
             v->codeAppendf("uint innerShapeType = ((%s & INNER_SHAPE_TYPE_MASK) >> "
                                                   "INNER_SHAPE_TYPE_BIT);",
                            inputs.attr(Attrib::kInstanceInfo));
@@ -346,13 +346,13 @@
     }
 
     if (usedShapeDefinitions & kOval_ShapeFlag) {
-        v->definef("OVAL_SHAPE_TYPE", "%du", (int)ShapeType::kOval);
+        v->defineConstant("OVAL_SHAPE_TYPE", (int)ShapeType::kOval);
     }
     if (usedShapeDefinitions & kSimpleRRect_ShapeFlag) {
-        v->definef("SIMPLE_R_RECT_SHAPE_TYPE", "%du", (int)ShapeType::kSimpleRRect);
+        v->defineConstant("SIMPLE_R_RECT_SHAPE_TYPE", (int)ShapeType::kSimpleRRect);
     }
     if (usedShapeDefinitions & kNinePatch_ShapeFlag) {
-        v->definef("NINE_PATCH_SHAPE_TYPE", "%du", (int)ShapeType::kNinePatch);
+        v->defineConstant("NINE_PATCH_SHAPE_TYPE", (int)ShapeType::kNinePatch);
     }
     SkASSERT(!(usedShapeDefinitions & (kRect_ShapeFlag | kComplexRRect_ShapeFlag)));
 
@@ -367,8 +367,8 @@
                        inputs.attr(Attrib::kLocalRect), inputs.attr(Attrib::kLocalRect));
     }
     if (ip.batchInfo().fHasLocalMatrix && ip.batchInfo().fHasParams) {
-        v->definef("LOCAL_MATRIX_FLAG", "0x%xu", kLocalMatrix_InfoFlag);
-        v->codeAppendf("if (0u != (%s & LOCAL_MATRIX_FLAG)) {",
+        v->defineConstantf("int", "LOCAL_MATRIX_FLAG", "0x%x", kLocalMatrix_InfoFlag);
+        v->codeAppendf("if (0 != (%s & LOCAL_MATRIX_FLAG)) {",
                        inputs.attr(Attrib::kInstanceInfo));
         if (!ip.batchInfo().fUsesLocalCoords) {
             inputs.skipParams(2);
@@ -1179,7 +1179,7 @@
             }
         }
         if (kRect_ShapeFlag != fBatchInfo.fShapeTypes) {
-           v->definef("SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
+           v->defineConstantf("int", "SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
            varyingHandler->addFlatVarying("earlyAccept", &fEarlyAccept, kHigh_GrSLPrecision);
         }
     }
@@ -1360,10 +1360,10 @@
 void GLSLInstanceProcessor::BackendMultisample::onEmitCode(GrGLSLVertexBuilder*,
                                                            GrGLSLPPFragmentBuilder* f,
                                                            const char*, const char*) {
-    f->define("SAMPLE_COUNT", fEffectiveSampleCnt);
+    f->defineConstant("SAMPLE_COUNT", fEffectiveSampleCnt);
     if (this->isMixedSampled()) {
-        f->definef("SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
-        f->definef("SAMPLE_MASK_MSB", "0x%x", 1 << (fEffectiveSampleCnt - 1));
+        f->defineConstantf("int", "SAMPLE_MASK_ALL", "0x%x", (1 << fEffectiveSampleCnt) - 1);
+        f->defineConstantf("int", "SAMPLE_MASK_MSB", "0x%x", 1 << (fEffectiveSampleCnt - 1));
     }
 
     if (kRect_ShapeFlag != (fBatchInfo.fShapeTypes | fBatchInfo.fInnerShapeTypes)) {
diff --git a/src/sksl/README b/src/sksl/README
new file mode 100644
index 0000000..1427c21
--- /dev/null
+++ b/src/sksl/README
@@ -0,0 +1,35 @@
+Overview
+========
+
+SkSL ("Skia Shading Language") is a variant of GLSL which is used as Skia's 
+internal shading language. SkSL is, at its heart, a single standardized version
+of GLSL which avoids all of the various version and dialect differences found
+in GLSL "in the wild", but it does bring a few of its own changes to the table.
+
+Skia uses the SkSL compiler to convert SkSL code to GLSL, GLSL ES, or SPIR-V
+before handing it over to the graphics driver.
+
+Differences from GLSL
+=====================
+
+SkSL is based on GLSL 4.5. For the most part, write SkSL exactly as you would
+desktop GLSL, and the SkSL compiler will take care of version and dialect
+differences (for instance, you always use "in" and "out", and skslc will handle
+translating them to "varying" and "attribute" as appropriate). Be aware of the 
+following differences between SkSL and GLSL:
+
+* no #version or "precision" statement is required, and they will be ignored if
+  present
+* the output color is sk_FragColor (do not declare it)
+* lowp, mediump, and highp are always permitted (but will only be respected if 
+  you run on a GLES device)
+* you do not need to include ".0" to make a number a float (meaning that
+  "vec2(x, y) * 4" is perfectly legal in SkSL, unlike GLSL where it would often
+  have to be expressed "vec2(x, y) * 4.0". There is no performance penalty for 
+  this, as the number is converted to a float at compile time)
+* type suffixes on numbers (1.0f, 0xFFu) are both unnecessary and unsupported
+* some built-in functions and one or two rarely-used language features are not
+  yet supported (sorry!)
+
+SkSL is still under development, and is expected to diverge further from GLSL
+over time.
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index d2ad812..b57e673 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -15,6 +15,7 @@
 #include "SkSLSPIRVCodeGenerator.h"
 #include "ir/SkSLExpression.h"
 #include "ir/SkSLIntLiteral.h"
+#include "ir/SkSLModifiersDeclaration.h"
 #include "ir/SkSLSymbolTable.h"
 #include "ir/SkSLVarDeclaration.h"
 #include "SkMutex.h"
@@ -97,6 +98,7 @@
     ADD_TYPE(Sampler1D);
     ADD_TYPE(Sampler2D);
     ADD_TYPE(Sampler3D);
+    ADD_TYPE(SamplerExternalOES);
     ADD_TYPE(SamplerCube);
     ADD_TYPE(Sampler2DRect);
     ADD_TYPE(Sampler1DArray);
@@ -130,6 +132,7 @@
 
     std::vector<std::unique_ptr<ProgramElement>> ignored;
     this->internalConvertProgram(SKSL_INCLUDE, &ignored);
+    printf("%s", errorText().c_str());
     ASSERT(!fErrorCount);
 }
 
@@ -164,6 +167,14 @@
                 }
                 break;
             }
+            case ASTDeclaration::kModifiers_Kind: {
+                std::unique_ptr<ModifiersDeclaration> f = fIRGenerator->convertModifiersDeclaration(
+                                                                   (ASTModifiersDeclaration&) decl);
+                if (f) {
+                    result->push_back(std::move(f));
+                }
+                break;
+            }
             case ASTDeclaration::kInterfaceBlock_Kind: {
                 std::unique_ptr<InterfaceBlock> i = fIRGenerator->convertInterfaceBlock(
                                                                          (ASTInterfaceBlock&) decl);
diff --git a/src/sksl/SkSLCompiler.h b/src/sksl/SkSLCompiler.h
index 9cd1eac..7c8cf81 100644
--- a/src/sksl/SkSLCompiler.h
+++ b/src/sksl/SkSLCompiler.h
@@ -15,6 +15,8 @@
 #include "SkSLErrorReporter.h"
 #include "SkSLGLSLCodeGenerator.h"
 
+#define SK_FRAGCOLOR_BUILTIN 10001
+
 namespace SkSL {
 
 class IRGenerator;
@@ -24,6 +26,8 @@
  * file into an abstract syntax tree (a tree of ASTNodes), then performs semantic analysis to 
  * produce a Program (a tree of IRNodes), then feeds the Program into a CodeGenerator to produce
  * compiled output.
+ *
+ * See the README for information about SkSL.
  */
 class Compiler : public ErrorReporter {
 public:
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 1f124d0..82c265b 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -60,6 +60,7 @@
     , fSampler1D_Type(new Type("sampler1D", SpvDim1D, false, false, false, true))
     , fSampler2D_Type(new Type("sampler2D", SpvDim2D, false, false, false, true))
     , fSampler3D_Type(new Type("sampler3D", SpvDim3D, false, false, false, true))
+    , fSamplerExternalOES_Type(new Type("samplerExternalOES", SpvDim2D, false, false, false, true))
     , fSamplerCube_Type(new Type("samplerCube"))
     , fSampler2DRect_Type(new Type("sampler2DRect"))
     , fSampler1DArray_Type(new Type("sampler1DArray"))
@@ -169,6 +170,7 @@
     const std::unique_ptr<Type> fSampler1D_Type;
     const std::unique_ptr<Type> fSampler2D_Type;
     const std::unique_ptr<Type> fSampler3D_Type;
+    const std::unique_ptr<Type> fSamplerExternalOES_Type;
     const std::unique_ptr<Type> fSamplerCube_Type;
     const std::unique_ptr<Type> fSampler2DRect_Type;
     const std::unique_ptr<Type> fSampler1DArray_Type;
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index da0bcb9..6a82cf2 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -14,8 +14,11 @@
 #include "ir/SkSLExpressionStatement.h"
 #include "ir/SkSLExtension.h"
 #include "ir/SkSLIndexExpression.h"
+#include "ir/SkSLModifiersDeclaration.h"
 #include "ir/SkSLVariableReference.h"
 
+#define SK_FRAGCOLOR_BUILTIN 10001
+
 namespace SkSL {
 
 void GLSLCodeGenerator::write(const char* s) {
@@ -66,7 +69,7 @@
         this->writeLine("struct " + type.name() + " {");
         fIndentation++;
         for (const auto& f : type.fields()) {
-            this->writeModifiers(f.fModifiers);
+            this->writeModifiers(f.fModifiers, false);
             // sizes (which must be static in structs) are part of the type name here
             this->writeType(*f.fType);
             this->writeLine(" " + f.fName + ";");
@@ -124,7 +127,40 @@
     }
 }
 
+static bool is_abs(Expression& expr) {
+    if (expr.fKind != Expression::kFunctionCall_Kind) {
+        return false;
+    }
+    return ((FunctionCall&) expr).fFunction.fName == "abs";
+}
+
+// turns min(abs(x), y) into (abs(x) > (tmpVar = y) ? tmpVar : abs(x)) to avoid a Tegra3 compiler 
+// bug.
+void GLSLCodeGenerator::writeMinAbsHack(Expression& absExpr, Expression& otherExpr) {
+    ASSERT(!fCaps.fCanUseMinAndAbsTogether);
+    std::string varName = "minAbsHackVar" + to_string(fVarCount++);
+    this->fFunctionHeader += "    " + otherExpr.fType.name() + " " + varName + ";\n";
+    this->write("(");
+    this->writeExpression(absExpr, kTopLevel_Precedence);
+    this->write(" > (" + varName + " = ");
+    this->writeExpression(otherExpr, kRelational_Precedence);
+    this->write(") ? " + varName + " : ");
+    this->writeExpression(absExpr, kTernary_Precedence);
+    this->write(")");
+}
+
 void GLSLCodeGenerator::writeFunctionCall(const FunctionCall& c) {
+    if (!fCaps.fCanUseMinAndAbsTogether && c.fFunction.fName == "min") {
+        ASSERT(c.fArguments.size() == 2);
+        if (is_abs(*c.fArguments[0])) {
+            this->writeMinAbsHack(*c.fArguments[0], *c.fArguments[1]);
+            return;
+        }
+        if (is_abs(*c.fArguments[1])) {
+            this->writeMinAbsHack(*c.fArguments[1], *c.fArguments[0]);
+            return;
+        }
+    }
     this->write(c.fFunction.fName + "(");
     const char* separator = "";
     for (const auto& arg : c.fArguments) {
@@ -147,7 +183,15 @@
 }
 
 void GLSLCodeGenerator::writeVariableReference(const VariableReference& ref) {
-    this->write(ref.fVariable.fName);
+    if (ref.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN) {
+        if (fCaps.fMustDeclareFragmentShaderOutput) {
+            this->write("sk_FragColor");
+        } else {
+            this->write("gl_FragColor");
+        }
+    } else {
+        this->write(ref.fVariable.fName);
+    }
 }
 
 void GLSLCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
@@ -270,7 +314,11 @@
 }
 
 void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) {
-    this->write(to_string(i.fValue));
+    if (i.fType == *fContext.fUInt_Type) {
+        this->write(to_string(i.fValue & 0xffffffff) + "u");
+    } else {
+        this->write(to_string(i.fValue));
+    }
 }
 
 void GLSLCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
@@ -284,28 +332,96 @@
     for (const auto& param : f.fDeclaration.fParameters) {
         this->write(separator);
         separator = ", ";
-        this->writeModifiers(param->fModifiers);
-        this->writeType(param->fType);
+        this->writeModifiers(param->fModifiers, false);
+        std::vector<int> sizes;
+        const Type* type = &param->fType;
+        while (type->kind() == Type::kArray_Kind) {
+            sizes.push_back(type->columns());
+            type = &type->componentType();
+        }
+        this->writeType(*type);
         this->write(" " + param->fName);
+        for (int s : sizes) {
+            if (s <= 0) {
+                this->write("[]");
+            } else {
+                this->write("[" + to_string(s) + "]");
+            }
+        }
     }
-    this->write(") ");
-    this->writeBlock(*f.fBody);
-    this->writeLine();
+    this->writeLine(") {");
+
+    fFunctionHeader = "";
+    std::ostream* oldOut = fOut;
+    std::stringstream buffer;
+    fOut = &buffer;
+    fIndentation++;
+    for (const auto& s : f.fBody->fStatements) {
+        this->writeStatement(*s);
+        this->writeLine();
+    }
+    fIndentation--;
+    this->writeLine("}");
+
+    fOut = oldOut;
+    this->write(fFunctionHeader);
+    this->write(buffer.str());
 }
 
-void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers) {
-    this->write(modifiers.description());
+void GLSLCodeGenerator::writeModifiers(const Modifiers& modifiers, 
+                                       bool globalContext) {
+    if (modifiers.fFlags & Modifiers::kNoPerspective_Flag) {
+        this->write("noperspective ");
+    }
+    if (modifiers.fFlags & Modifiers::kFlat_Flag) {
+        this->write("flat ");
+    }
+    std::string layout = modifiers.fLayout.description();
+    if (layout.length()) {
+        this->write(layout + " ");
+    }
+    if ((modifiers.fFlags & Modifiers::kIn_Flag) && 
+        (modifiers.fFlags & Modifiers::kOut_Flag)) {
+        this->write("inout ");
+    } else if (modifiers.fFlags & Modifiers::kIn_Flag) {
+        if (globalContext && fCaps.fVersion < 130) {
+            this->write(fProgramKind == Program::kVertex_Kind ? "attribute "
+                                                              : "varying ");
+        } else {
+            this->write("in ");
+        }
+    } else if (modifiers.fFlags & Modifiers::kOut_Flag) {
+        if (globalContext && fCaps.fVersion < 130) {
+            this->write("varying ");
+        } else {
+            this->write("out ");
+        }
+    }
+    if (modifiers.fFlags & Modifiers::kUniform_Flag) {
+        this->write("uniform ");
+    }
+    if (modifiers.fFlags & Modifiers::kConst_Flag) {
+        this->write("const ");
+    }
+    if (fCaps.fUsesPrecisionModifiers) {
+        if (modifiers.fFlags & Modifiers::kLowp_Flag) {
+            this->write("lowp ");
+        }
+        if (modifiers.fFlags & Modifiers::kHighp_Flag) {
+            this->write("highp ");
+        }
+    }
 }
 
 void GLSLCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf) {
     if (intf.fVariable.fName == "gl_PerVertex") {
         return;
     }
-    this->writeModifiers(intf.fVariable.fModifiers);
+    this->writeModifiers(intf.fVariable.fModifiers, true);
     this->writeLine(intf.fVariable.fType.name() + " {");
     fIndentation++;
     for (const auto& f : intf.fVariable.fType.fields()) {
-        this->writeModifiers(f.fModifiers);
+        this->writeModifiers(f.fModifiers, false);
         this->writeType(*f.fType);
         this->writeLine(" " + f.fName + ";");
     }
@@ -313,9 +429,9 @@
     this->writeLine("};");
 }
 
-void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl) {
+void GLSLCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
     ASSERT(decl.fVars.size() > 0);
-    this->writeModifiers(decl.fVars[0].fVar->fModifiers);
+    this->writeModifiers(decl.fVars[0].fVar->fModifiers, global);
     this->writeType(decl.fBaseType);
     std::string separator = " ";
     for (const auto& var : decl.fVars) {
@@ -325,7 +441,9 @@
         this->write(var.fVar->fName);
         for (const auto& size : var.fSizes) {
             this->write("[");
-            this->writeExpression(*size, kTopLevel_Precedence);
+            if (size) {
+                this->writeExpression(*size, kTopLevel_Precedence);
+            }
             this->write("]");
         }
         if (var.fValue) {
@@ -349,7 +467,7 @@
             this->writeReturnStatement((ReturnStatement&) s);
             break;
         case Statement::kVarDeclarations_Kind:
-            this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration);
+            this->writeVarDeclarations(*((VarDeclarationsStatement&) s).fDeclaration, false);
             break;
         case Statement::kIf_Kind:
             this->writeIfStatement((IfStatement&) s);
@@ -444,22 +562,42 @@
 void GLSLCodeGenerator::generateCode(const Program& program, std::ostream& out) {
     ASSERT(fOut == nullptr);
     fOut = &out;
+    fProgramKind = program.fKind;
     this->write("#version " + to_string(fCaps.fVersion));
-    if (fCaps.fStandard == GLCaps::kGLES_Standard) {
+    if (fCaps.fStandard == GLCaps::kGLES_Standard && fCaps.fVersion >= 300) {
         this->write(" es");
+    } else if (fCaps.fIsCoreProfile) {
+        this->write(" core");
     }
     this->writeLine();
     for (const auto& e : program.fElements) {
+        if (e->fKind == ProgramElement::kExtension_Kind) {
+            this->writeExtension((Extension&) *e);
+        }
+    }
+    if (fCaps.fStandard == GLCaps::kGLES_Standard) {
+        this->writeLine("precision mediump float;");
+    }
+    for (const auto& e : program.fElements) {
         switch (e->fKind) {
             case ProgramElement::kExtension_Kind:
-                this->writeExtension((Extension&) *e);
                 break;
             case ProgramElement::kVar_Kind: {
                 VarDeclarations& decl = (VarDeclarations&) *e;
-                if (decl.fVars.size() > 0 && 
-                    decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin == -1) {
-                    this->writeVarDeclarations(decl);
-                    this->writeLine();
+                if (decl.fVars.size() > 0) {
+                    int builtin = decl.fVars[0].fVar->fModifiers.fLayout.fBuiltin;
+                    if (builtin == -1) {
+                        // normal var
+                        this->writeVarDeclarations(decl, true);
+                        this->writeLine();
+                    } else if (builtin == SK_FRAGCOLOR_BUILTIN &&
+                               fCaps.fMustDeclareFragmentShaderOutput) {
+                        this->write("out ");
+                        if (fCaps.fUsesPrecisionModifiers) {
+                            this->write("mediump ");
+                        }
+                        this->writeLine("vec4 sk_FragColor;");
+                    }
                 }
                 break;
             }
@@ -469,6 +607,10 @@
             case ProgramElement::kFunction_Kind:
                 this->writeFunction((FunctionDefinition&) *e);
                 break;
+            case ProgramElement::kModifiers_Kind:
+                this->writeModifiers(((ModifiersDeclaration&) *e).fModifiers, true);
+                this->writeLine(";");
+                break;
             default:
                 printf("%s\n", e->description().c_str());
                 ABORT("unsupported program element");
diff --git a/src/sksl/SkSLGLSLCodeGenerator.h b/src/sksl/SkSLGLSLCodeGenerator.h
index 3534aff..9851123 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.h
+++ b/src/sksl/SkSLGLSLCodeGenerator.h
@@ -50,6 +50,11 @@
         kGL_Standard,
         kGLES_Standard
     } fStandard;
+    bool fIsCoreProfile;
+    bool fUsesPrecisionModifiers;
+    bool fMustDeclareFragmentShaderOutput;
+    // The Tegra3 compiler will sometimes never return if we have min(abs(x), y)
+    bool fCanUseMinAndAbsTogether;
 };
 
 /**
@@ -81,6 +86,8 @@
     GLSLCodeGenerator(const Context* context, GLCaps caps)
     : fContext(*context)
     , fCaps(caps)
+    , fOut(nullptr)
+    , fVarCount(0)
     , fIndentation(0)
     , fAtLineStart(true) {}
 
@@ -111,11 +118,11 @@
 
     void writeLayout(const Layout& layout);
 
-    void writeModifiers(const Modifiers& modifiers);
+    void writeModifiers(const Modifiers& modifiers, bool globalContext);
     
     void writeGlobalVars(const VarDeclaration& vs);
 
-    void writeVarDeclarations(const VarDeclarations& decl);
+    void writeVarDeclarations(const VarDeclarations& decl, bool global);
 
     void writeVariableReference(const VariableReference& ref);
 
@@ -123,6 +130,8 @@
     
     void writeIntrinsicCall(const FunctionCall& c);
 
+    void writeMinAbsHack(Expression& absExpr, Expression& otherExpr);
+
     void writeFunctionCall(const FunctionCall& c);
 
     void writeConstructor(const Constructor& c);
@@ -164,6 +173,9 @@
     const Context& fContext;
     const GLCaps fCaps;
     std::ostream* fOut;
+    std::string fFunctionHeader;
+    Program::Kind fProgramKind;
+    int fVarCount;
     int fIndentation;
     bool fAtLineStart;
     // Keeps track of which struct types we have written. Given that we are unlikely to ever write 
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index c30cac1..64cc2df 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -189,10 +189,16 @@
             }
             value = this->coerce(std::move(value), *type);
         }
-        if ("gl_FragCoord" == varDecl.fName && (*fSymbolTable)[varDecl.fName]) {
+        if ("sk_FragColor" == varDecl.fName && (*fSymbolTable)[varDecl.fName]) {
+            // already defined, ignore
+        } else if ((*fSymbolTable)[varDecl.fName] && 
+                   (*fSymbolTable)[varDecl.fName]->fKind == Symbol::kVariable_Kind && 
+                   ((Variable*) (*fSymbolTable)[varDecl.fName])->fModifiers.fLayout.fBuiltin >= 0) {
             // already defined, just update the modifiers
             Variable* old = (Variable*) (*fSymbolTable)[varDecl.fName];
+            int builtin = old->fModifiers.fLayout.fBuiltin;
             old->fModifiers = var->fModifiers;
+            old->fModifiers.fLayout.fBuiltin = builtin;
         } else {
             variables.emplace_back(var.get(), std::move(sizes), std::move(value));
             fSymbolTable->add(varDecl.fName, std::move(var));
@@ -203,6 +209,12 @@
                                                                 std::move(variables)));
 }
 
+std::unique_ptr<ModifiersDeclaration> IRGenerator::convertModifiersDeclaration(
+                                                                 const ASTModifiersDeclaration& m) {
+    Modifiers modifiers = this->convertModifiers(m.fModifiers);
+    return std::unique_ptr<ModifiersDeclaration>(new ModifiersDeclaration(modifiers));
+}
+
 std::unique_ptr<Statement> IRGenerator::convertIf(const ASTIfStatement& s) {
     std::unique_ptr<Expression> test = this->coerce(this->convertExpression(*s.fTest), 
                                                     *fContext.fBool_Type);
@@ -419,8 +431,9 @@
                     for (size_t i = 0; i < parameters.size(); i++) {
                         if (parameters[i]->fModifiers != other->fParameters[i]->fModifiers) {
                             fErrors.error(f.fPosition, "modifiers on parameter " + 
-                                                       to_string(i + 1) + " differ between " +
-                                                       "declaration and definition");
+                                                       to_string((uint64_t) i + 1) + 
+                                                       " differ between declaration and "
+                                                       "definition");
                             return nullptr;
                         }
                     }
@@ -616,8 +629,9 @@
         ASSERT(ctor);
         return this->call(Position(), std::move(ctor), std::move(args));
     }
-    ABORT("cannot coerce %s to %s", expr->fType.description().c_str(), 
-          type.description().c_str());
+    std::vector<std::unique_ptr<Expression>> args;
+    args.push_back(std::move(expr));
+    return std::unique_ptr<Expression>(new Constructor(Position(), type, std::move(args)));
 }
 
 static bool is_matrix_multiply(const Type& left, const Type& right) {
@@ -832,12 +846,12 @@
                                               std::vector<std::unique_ptr<Expression>> arguments) {
     if (function.fParameters.size() != arguments.size()) {
         std::string msg = "call to '" + function.fName + "' expected " + 
-                                 to_string(function.fParameters.size()) + 
+                                 to_string((uint64_t) function.fParameters.size()) + 
                                  " argument";
         if (function.fParameters.size() != 1) {
             msg += "s";
         }
-        msg += ", but found " + to_string(arguments.size());
+        msg += ", but found " + to_string((uint64_t) arguments.size());
         fErrors.error(position, msg);
         return nullptr;
     }
@@ -921,7 +935,8 @@
                                                     std::vector<std::unique_ptr<Expression>> args) {
     // FIXME: add support for structs and arrays
     Type::Kind kind = type.kind();
-    if (!type.isNumber() && kind != Type::kVector_Kind && kind != Type::kMatrix_Kind) {
+    if (!type.isNumber() && kind != Type::kVector_Kind && kind != Type::kMatrix_Kind && 
+        kind != Type::kArray_Kind) {
         fErrors.error(position, "cannot construct '" + type.description() + "'");
         return nullptr;
     }
@@ -938,7 +953,7 @@
         if (args.size() != 1) {
             fErrors.error(position, "invalid arguments to '" + type.description() + 
                                     "' constructor, (expected exactly 1 argument, but found " +
-                                    to_string(args.size()) + ")");
+                                    to_string((uint64_t) args.size()) + ")");
         }
         if (args[0]->fType == *fContext.fBool_Type) {
             std::unique_ptr<IntLiteral> zero(new IntLiteral(fContext, position, 0));
@@ -953,6 +968,18 @@
                                     "' constructor (expected a number or bool, but found '" +
                                     args[0]->fType.description() + "')");
         }
+        if (args[0]->fKind == Expression::kIntLiteral_Kind && (type == *fContext.fInt_Type || 
+            type == *fContext.fUInt_Type)) {
+            return std::unique_ptr<Expression>(new IntLiteral(fContext, 
+                                                              position, 
+                                                              ((IntLiteral&) *args[0]).fValue,
+                                                              &type));
+        }
+    } else if (kind == Type::kArray_Kind) {
+        const Type& base = type.componentType();
+        for (size_t i = 0; i < args.size(); i++) {
+            args[i] = this->coerce(std::move(args[i]), base);
+        }
     } else {
         ASSERT(kind == Type::kVector_Kind || kind == Type::kMatrix_Kind);
         int actual = 0;
@@ -1054,7 +1081,8 @@
 
 std::unique_ptr<Expression> IRGenerator::convertIndex(std::unique_ptr<Expression> base,
                                                       const ASTExpression& index) {
-    if (base->fType.kind() != Type::kArray_Kind && base->fType.kind() != Type::kMatrix_Kind) {
+    if (base->fType.kind() != Type::kArray_Kind && base->fType.kind() != Type::kMatrix_Kind &&
+            base->fType.kind() != Type::kVector_Kind) {
         fErrors.error(base->fPosition, "expected array, but found '" + base->fType.description() + 
                                        "'");
         return nullptr;
@@ -1063,9 +1091,11 @@
     if (!converted) {
         return nullptr;
     }
-    converted = this->coerce(std::move(converted), *fContext.fInt_Type);
-    if (!converted) {
-        return nullptr;
+    if (converted->fType != *fContext.fUInt_Type) {
+        converted = this->coerce(std::move(converted), *fContext.fInt_Type);
+        if (!converted) {
+            return nullptr;
+        }
     }
     return std::unique_ptr<Expression>(new IndexExpression(fContext, std::move(base), 
                                                            std::move(converted)));
@@ -1143,9 +1173,21 @@
         return nullptr;
     }
     switch (expression.fSuffix->fKind) {
-        case ASTSuffix::kIndex_Kind:
-            return this->convertIndex(std::move(base), 
-                                      *((ASTIndexSuffix&) *expression.fSuffix).fExpression);
+        case ASTSuffix::kIndex_Kind: {
+            const ASTExpression* expr = ((ASTIndexSuffix&) *expression.fSuffix).fExpression.get();
+            if (expr) {
+                return this->convertIndex(std::move(base), *expr);
+            } else if (base->fKind == Expression::kTypeReference_Kind) {
+                const Type& oldType = ((TypeReference&) *base).fValue;
+                Type* newType = new Type(oldType.name() + "[]", Type::kArray_Kind, oldType, 
+                                         -1);
+                fSymbolTable->takeOwnership(newType);
+                return std::unique_ptr<Expression>(new TypeReference(fContext, base->fPosition, 
+                                                                     *newType));
+            } else {
+                fErrors.error(expression.fPosition, "'[]' must follow a type name");
+            }
+        }
         case ASTSuffix::kCall_Kind: {
             auto rawArguments = &((ASTCallSuffix&) *expression.fSuffix).fArguments;
             std::vector<std::unique_ptr<Expression>> arguments;
diff --git a/src/sksl/SkSLIRGenerator.h b/src/sksl/SkSLIRGenerator.h
index a3ff210..834ed8d 100644
--- a/src/sksl/SkSLIRGenerator.h
+++ b/src/sksl/SkSLIRGenerator.h
@@ -25,6 +25,7 @@
 #include "ast/SkSLASTIfStatement.h"
 #include "ast/SkSLASTInterfaceBlock.h"
 #include "ast/SkSLASTModifiers.h"
+#include "ast/SkSLASTModifiersDeclaration.h"
 #include "ast/SkSLASTPrefixExpression.h"
 #include "ast/SkSLASTReturnStatement.h"
 #include "ast/SkSLASTStatement.h"
@@ -39,6 +40,7 @@
 #include "ir/SkSLFunctionDefinition.h"
 #include "ir/SkSLInterfaceBlock.h"
 #include "ir/SkSLModifiers.h"
+#include "ir/SkSLModifiersDeclaration.h"
 #include "ir/SkSLSymbolTable.h"
 #include "ir/SkSLStatement.h"
 #include "ir/SkSLType.h"
@@ -61,6 +63,8 @@
     std::unique_ptr<FunctionDefinition> convertFunction(const ASTFunction& f);
     std::unique_ptr<Statement> convertStatement(const ASTStatement& statement);
     std::unique_ptr<Expression> convertExpression(const ASTExpression& expression);
+    std::unique_ptr<ModifiersDeclaration> convertModifiersDeclaration(
+                                                                  const ASTModifiersDeclaration& m);
 
 private:
     void pushSymbolTable();
diff --git a/src/sksl/SkSLMain.cpp b/src/sksl/SkSLMain.cpp
index 24fbb6c..de1b981 100644
--- a/src/sksl/SkSLMain.cpp
+++ b/src/sksl/SkSLMain.cpp
@@ -9,6 +9,24 @@
 #include <fstream>
 #include "SkSLCompiler.h"
 
+bool endsWith(const std::string& s, const std::string& ending) {
+    if (s.length() >= ending.length()) {
+        return (0 == s.compare(s.length() - ending.length(), ending.length(), ending));
+    }
+    return false;
+}
+
+static SkSL::GLCaps default_caps() {
+    return { 
+             400, 
+             SkSL::GLCaps::kGL_Standard,
+             false, // isCoreProfile
+             false, // usesPrecisionModifiers;
+             false, // mustDeclareFragmentShaderOutput
+             true   // canUseMinAndAbsTogether
+           };
+}
+
 /**
  * Very simple standalone executable to facilitate testing.
  */
@@ -35,14 +53,30 @@
         printf("error reading '%s'\n", argv[1]);
         exit(2);
     }
-    std::ofstream out(argv[2], std::ofstream::binary);
-    SkSL::Compiler compiler;
-    if (!compiler.toSPIRV(kind, text, out)) {
-        printf("%s", compiler.errorText().c_str());
-        exit(3);
-    }
-    if (out.rdstate()) {
-        printf("error writing '%s'\n", argv[2]);
-        exit(4);
+    std::string name(argv[2]);
+    if (endsWith(name, ".spirv")) {
+        std::ofstream out(argv[2], std::ofstream::binary);
+        SkSL::Compiler compiler;
+        if (!compiler.toSPIRV(kind, text, out)) {
+            printf("%s", compiler.errorText().c_str());
+            exit(3);
+        }
+        if (out.rdstate()) {
+            printf("error writing '%s'\n", argv[2]);
+            exit(4);
+        }
+    } else if (endsWith(name, ".glsl")) {
+        std::ofstream out(argv[2], std::ofstream::binary);
+        SkSL::Compiler compiler;
+        if (!compiler.toGLSL(kind, text, default_caps(), out)) {
+            printf("%s", compiler.errorText().c_str());
+            exit(3);
+        }
+        if (out.rdstate()) {
+            printf("error writing '%s'\n", argv[2]);
+            exit(4);
+        }
+    } else {
+        printf("expected output filename to end with '.spirv' or '.glsl'");
     }
 }
diff --git a/src/sksl/SkSLParser.cpp b/src/sksl/SkSLParser.cpp
index b240e45..cc133a8 100644
--- a/src/sksl/SkSLParser.cpp
+++ b/src/sksl/SkSLParser.cpp
@@ -56,6 +56,7 @@
 #include "ast/SkSLASTIndexSuffix.h"
 #include "ast/SkSLASTInterfaceBlock.h"
 #include "ast/SkSLASTIntLiteral.h"
+#include "ast/SkSLASTModifiersDeclaration.h"
 #include "ast/SkSLASTParameter.h"
 #include "ast/SkSLASTPrefixExpression.h"
 #include "ast/SkSLASTReturnStatement.h"
@@ -185,7 +186,8 @@
     this->expect(Token::SEMICOLON, "';'");
 }
 
-/* DIRECTIVE(#version) INT_LITERAL | DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */
+/* DIRECTIVE(#version) INT_LITERAL ("es" | "compatibility")? | 
+   DIRECTIVE(#extension) IDENTIFIER COLON IDENTIFIER */
 std::unique_ptr<ASTDeclaration> Parser::directive() {
     Token start;
     if (!this->expect(Token::DIRECTIVE, "a directive", &start)) {
@@ -193,7 +195,12 @@
     }
     if (start.fText == "#version") {
         this->expect(Token::INT_LITERAL, "a version number");
-        // ignored for now
+        Token next = this->peek();
+        if (next.fText == "es" || next.fText == "compatibility") {
+            this->nextToken();
+        }
+        // version is ignored for now; it will eventually become an error when we stop pretending
+        // to be GLSL
         return nullptr;
     } else if (start.fText == "#extension") {
         Token name;
@@ -227,6 +234,10 @@
     if (lookahead.fKind == Token::STRUCT) {
         return this->structVarDeclaration(modifiers);
     }
+    if (lookahead.fKind == Token::SEMICOLON) {
+        this->nextToken();
+        return std::unique_ptr<ASTDeclaration>(new ASTModifiersDeclaration(modifiers));
+    }
     std::unique_ptr<ASTType> type(this->type());
     if (!type) {
         return nullptr;
@@ -477,10 +488,13 @@
     int set = -1;
     int builtin = -1;
     bool originUpperLeft = false;
+    bool overrideCoverage = false;
+    bool blendSupportAllEquations = false;
     if (this->peek().fKind == Token::LAYOUT) {
         this->nextToken();
         if (!this->expect(Token::LPAREN, "'('")) {
-            return ASTLayout(location, binding, index, set, builtin, originUpperLeft);
+            return ASTLayout(location, binding, index, set, builtin, originUpperLeft,
+                             overrideCoverage, blendSupportAllEquations);
         }
         for (;;) {
             Token t = this->nextToken();
@@ -496,6 +510,10 @@
                 builtin = this->layoutInt();
             } else if (t.fText == "origin_upper_left") {
                 originUpperLeft = true;
+            } else if (t.fText == "override_coverage") {
+                overrideCoverage = true;
+            } else if (t.fText == "blend_support_all_equations") {
+                blendSupportAllEquations = true;
             } else {
                 this->error(t.fPosition, ("'" + t.fText + 
                                           "' is not a valid layout qualifier").c_str());
@@ -509,7 +527,8 @@
             }
         }
     }
-    return ASTLayout(location, binding, index, set, builtin, originUpperLeft);
+    return ASTLayout(location, binding, index, set, builtin, originUpperLeft, overrideCoverage,
+                     blendSupportAllEquations);
 }
 
 /* layout? (UNIFORM | CONST | IN | OUT | INOUT | LOWP | MEDIUMP | HIGHP | FLAT | NOPERSPECTIVE)* */
@@ -1254,12 +1273,16 @@
     }
 }
 
-/* LBRACKET expression RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN | 
+/* LBRACKET expression? RBRACKET | DOT IDENTIFIER | LPAREN parameters RPAREN | 
    PLUSPLUS | MINUSMINUS */
 std::unique_ptr<ASTSuffix> Parser::suffix() {
     Token next = this->nextToken();
     switch (next.fKind) {
         case Token::LBRACKET: {
+            if (this->peek().fKind == Token::RBRACKET) {
+                this->nextToken();
+                return std::unique_ptr<ASTSuffix>(new ASTIndexSuffix(next.fPosition));
+            }
             std::unique_ptr<ASTExpression> e = this->expression();
             if (!e) {
                 return nullptr;
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index d17e3c4..f13fc7e 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -15,6 +15,7 @@
 #include "ir/SkSLExtension.h"
 #include "ir/SkSLIndexExpression.h"
 #include "ir/SkSLVariableReference.h"
+#include "SkSLCompiler.h"
 
 namespace SkSL {
 
@@ -2321,7 +2322,7 @@
         this->writeInstruction(SpvOpDecorate, target, SpvDecorationDescriptorSet, layout.fSet, 
                                fDecorationBuffer);
     }
-    if (layout.fBuiltin >= 0) {
+    if (layout.fBuiltin >= 0 && layout.fBuiltin != SK_FRAGCOLOR_BUILTIN) {
         this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin, 
                                fDecorationBuffer);
     }
@@ -2363,10 +2364,19 @@
     return result;
 }
 
-void SPIRVCodeGenerator::writeGlobalVars(const VarDeclarations& decl, std::ostream& out) {
+#define BUILTIN_IGNORE 9999
+void SPIRVCodeGenerator::writeGlobalVars(Program::Kind kind, const VarDeclarations& decl, 
+                                         std::ostream& out) {
     for (size_t i = 0; i < decl.fVars.size(); i++) {
         const VarDeclaration& varDecl = decl.fVars[i];
         const Variable* var = varDecl.fVar;
+        if (var->fModifiers.fLayout.fBuiltin == BUILTIN_IGNORE) {
+            continue;
+        }
+        if (var->fModifiers.fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN &&
+            kind != Program::kFragment_Kind) {
+            continue;
+        }
         if (!var->fIsReadFrom && !var->fIsWrittenTo &&
                 !(var->fModifiers.fFlags & (Modifiers::kIn_Flag |
                                             Modifiers::kOut_Flag |
@@ -2562,7 +2572,8 @@
     }
     for (size_t i = 0; i < program.fElements.size(); i++) {
         if (program.fElements[i]->fKind == ProgramElement::kVar_Kind) {
-            this->writeGlobalVars(((VarDeclarations&) *program.fElements[i]), body);
+            this->writeGlobalVars(program.fKind, ((VarDeclarations&) *program.fElements[i]), 
+                                  body);
         }
     }
     for (size_t i = 0; i < program.fElements.size(); i++) {
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index e7b2b30..2800a86 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -115,7 +115,7 @@
 
     SpvId writeFunction(const FunctionDefinition& f, std::ostream& out);
 
-    void writeGlobalVars(const VarDeclarations& v, std::ostream& out);
+    void writeGlobalVars(Program::Kind kind, const VarDeclarations& v, std::ostream& out);
 
     void writeVarDeclarations(const VarDeclarations& decl, std::ostream& out);
 
diff --git a/src/sksl/SkSLUtil.cpp b/src/sksl/SkSLUtil.cpp
index 327bffe..2b51cff 100644
--- a/src/sksl/SkSLUtil.cpp
+++ b/src/sksl/SkSLUtil.cpp
@@ -9,7 +9,48 @@
 
 namespace SkSL {
 
+std::string to_string(double value) {
+    std::stringstream buffer;
+    buffer << std::setprecision(std::numeric_limits<double>::digits10) << value;
+    std::string result = buffer.str();
+    if (result.find_last_of(".") == std::string::npos && 
+        result.find_last_of("e") == std::string::npos) {
+        result += ".0";
+    }
+    return result;
+}
+
+std::string to_string(int32_t value) {
+    std::stringstream buffer;
+    buffer << value;
+    return buffer.str();
+}
+
+std::string to_string(uint32_t value) {
+    std::stringstream buffer;
+    buffer << value;
+    return buffer.str();
+}
+
+std::string to_string(int64_t value) {
+    std::stringstream buffer;
+    buffer << value;
+    return buffer.str();
+}
+
+std::string to_string(uint64_t value) {
+    std::stringstream buffer;
+    buffer << value;
+    return buffer.str();
+}
+
 int stoi(std::string s) {
+    if (s.size() > 2 && s[0] == '0' && s[1] == 'x') {
+        char* p;
+        int result = strtoul(s.substr(2).c_str(), &p, 16);
+        ASSERT(*p == 0);
+        return result;
+    }
     return atoi(s.c_str());
 }
 
@@ -18,6 +59,12 @@
 }
 
 long stol(std::string s) {
+    if (s.size() > 2 && s[0] == '0' && s[1] == 'x') {
+        char* p;
+        int result = strtoul(s.substr(2).c_str(), &p, 16);
+        ASSERT(*p == 0);
+        return result;
+    }
     return atol(s.c_str());
 }
 
diff --git a/src/sksl/SkSLUtil.h b/src/sksl/SkSLUtil.h
index 33611cd..efffaae 100644
--- a/src/sksl/SkSLUtil.h
+++ b/src/sksl/SkSLUtil.h
@@ -19,11 +19,15 @@
 
 // our own definitions of certain std:: functions, because they are not always present on Android
 
-template <typename T> std::string to_string(T value) {
-    std::stringstream buffer;
-    buffer << std::setprecision(std::numeric_limits<T>::digits10) << value;
-    return buffer.str();
-}
+std::string to_string(double value);
+
+std::string to_string(int32_t value);
+
+std::string to_string(uint32_t value);
+
+std::string to_string(int64_t value);
+
+std::string to_string(uint64_t value);
 
 #if _MSC_VER
 #define NORETURN __declspec(noreturn)
diff --git a/src/sksl/ast/SkSLASTDeclaration.h b/src/sksl/ast/SkSLASTDeclaration.h
index 8b55ecf..cd8aae1 100644
--- a/src/sksl/ast/SkSLASTDeclaration.h
+++ b/src/sksl/ast/SkSLASTDeclaration.h
@@ -20,7 +20,8 @@
         kVar_Kind,
         kFunction_Kind,
         kInterfaceBlock_Kind,
-        kExtension_Kind
+        kExtension_Kind,
+        kModifiers_Kind
     };
 
     ASTDeclaration(Position position, Kind kind)
diff --git a/src/sksl/ast/SkSLASTIndexSuffix.h b/src/sksl/ast/SkSLASTIndexSuffix.h
index 44d91fa..755029b 100644
--- a/src/sksl/ast/SkSLASTIndexSuffix.h
+++ b/src/sksl/ast/SkSLASTIndexSuffix.h
@@ -14,17 +14,27 @@
 namespace SkSL {
 
 /**
- * A bracketed expression, as in '[0]', indicating an array access. 
+ * A bracketed expression, as in '[0]', indicating an array access. Empty brackets (as occur in
+ * 'float[](5, 6)' are represented with a null fExpression.
  */
 struct ASTIndexSuffix : public ASTSuffix {
+    ASTIndexSuffix(Position position) 
+    : INHERITED(position, ASTSuffix::kIndex_Kind)
+    , fExpression(nullptr) {}
+
     ASTIndexSuffix(std::unique_ptr<ASTExpression> expression) 
-    : INHERITED(expression->fPosition, ASTSuffix::kIndex_Kind)
+    : INHERITED(expression ? expression->fPosition : Position(), ASTSuffix::kIndex_Kind)
     , fExpression(std::move(expression)) {}
 
     std::string description() const override {
-        return "[" + fExpression->description() + "]";
+        if (fExpression) {
+            return "[" + fExpression->description() + "]";
+        } else {
+            return "[]";
+        }
     }
 
+    // may be null
     std::unique_ptr<ASTExpression> fExpression;
 
     typedef ASTSuffix INHERITED;
diff --git a/src/sksl/ast/SkSLASTLayout.h b/src/sksl/ast/SkSLASTLayout.h
index 08d6753..515eb2b 100644
--- a/src/sksl/ast/SkSLASTLayout.h
+++ b/src/sksl/ast/SkSLASTLayout.h
@@ -20,13 +20,16 @@
  */
 struct ASTLayout : public ASTNode {
     // For all parameters, a -1 means no value
-    ASTLayout(int location, int binding, int index, int set, int builtin, bool originUpperLeft)
+    ASTLayout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
+              bool overrideCoverage, bool blendSupportAllEquations)
     : fLocation(location)
     , fBinding(binding)
     , fIndex(index)
     , fSet(set)
     , fBuiltin(builtin)
-    , fOriginUpperLeft(originUpperLeft) {}
+    , fOriginUpperLeft(originUpperLeft)
+    , fOverrideCoverage(overrideCoverage)
+    , fBlendSupportAllEquations(blendSupportAllEquations) {}
 
     std::string description() const {
         std::string result;
@@ -55,6 +58,14 @@
             result += separator + "origin_upper_left";
             separator = ", ";
         }
+        if (fOverrideCoverage) {
+            result += separator + "override_coverage";
+            separator = ", ";
+        }
+        if (fBlendSupportAllEquations) {
+            result += separator + "blend_support_all_equations";
+            separator = ", ";
+        }
         if (result.length() > 0) {
             result = "layout (" + result + ")";
         }
@@ -67,6 +78,8 @@
     const int fSet;
     const int fBuiltin;
     const bool fOriginUpperLeft;
+    const bool fOverrideCoverage;
+    const bool fBlendSupportAllEquations;
 };
 
 } // namespace
diff --git a/src/sksl/ast/SkSLASTModifiersDeclaration.h b/src/sksl/ast/SkSLASTModifiersDeclaration.h
new file mode 100644
index 0000000..f5cc620
--- /dev/null
+++ b/src/sksl/ast/SkSLASTModifiersDeclaration.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+ 
+#ifndef SKSL_ASTMODIFIERDECLARATION
+#define SKSL_ASTMODIFIERDECLARATION
+
+#include "SkSLASTDeclaration.h"
+#include "SkSLASTModifiers.h"
+
+namespace SkSL {
+
+/**
+ * A declaration that consists only of modifiers, e.g.:
+ *
+ * layout(blend_support_all_equations) out;
+ */
+struct ASTModifiersDeclaration : public ASTDeclaration {
+    ASTModifiersDeclaration(ASTModifiers modifiers)
+    : INHERITED(Position(), kModifiers_Kind)
+    , fModifiers(modifiers) {}
+
+    std::string description() const {
+        return fModifiers.description() + ";";
+    }
+
+    ASTModifiers fModifiers;
+
+    typedef ASTDeclaration INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLIndexExpression.h b/src/sksl/ir/SkSLIndexExpression.h
index f5b0d09..319e06f 100644
--- a/src/sksl/ir/SkSLIndexExpression.h
+++ b/src/sksl/ir/SkSLIndexExpression.h
@@ -19,7 +19,7 @@
 static const Type& index_type(const Context& context, const Type& type) {
     if (type.kind() == Type::kMatrix_Kind) {
         if (type.componentType() == *context.fFloat_Type) {
-            switch (type.columns()) {
+            switch (type.rows()) {
                 case 2: return *context.fVec2_Type;
                 case 3: return *context.fVec3_Type;
                 case 4: return *context.fVec4_Type;
@@ -47,7 +47,7 @@
     : INHERITED(base->fPosition, kIndex_Kind, index_type(context, base->fType))
     , fBase(std::move(base))
     , fIndex(std::move(index)) {
-        ASSERT(fIndex->fType == *context.fInt_Type);
+        ASSERT(fIndex->fType == *context.fInt_Type || fIndex->fType == *context.fUInt_Type);
     }
 
     std::string description() const override {
diff --git a/src/sksl/ir/SkSLIntLiteral.h b/src/sksl/ir/SkSLIntLiteral.h
index 2bc5657..8921c28 100644
--- a/src/sksl/ir/SkSLIntLiteral.h
+++ b/src/sksl/ir/SkSLIntLiteral.h
@@ -18,8 +18,8 @@
 struct IntLiteral : public Expression {
     // FIXME: we will need to revisit this if/when we add full support for both signed and unsigned
     // 64-bit integers, but for right now an int64_t will hold every value we care about
-    IntLiteral(const Context& context, Position position, int64_t value)
-    : INHERITED(position, kIntLiteral_Kind, *context.fInt_Type)
+    IntLiteral(const Context& context, Position position, int64_t value, const Type* type = nullptr)
+    : INHERITED(position, kIntLiteral_Kind, type ? *type : *context.fInt_Type)
     , fValue(value) {}
 
     virtual std::string description() const override {
diff --git a/src/sksl/ir/SkSLLayout.h b/src/sksl/ir/SkSLLayout.h
index d8dc980..24087d0 100644
--- a/src/sksl/ir/SkSLLayout.h
+++ b/src/sksl/ir/SkSLLayout.h
@@ -22,15 +22,20 @@
     , fIndex(layout.fIndex)
     , fSet(layout.fSet)
     , fBuiltin(layout.fBuiltin)
-    , fOriginUpperLeft(layout.fOriginUpperLeft) {}
+    , fOriginUpperLeft(layout.fOriginUpperLeft)
+    , fOverrideCoverage(layout.fOverrideCoverage)
+    , fBlendSupportAllEquations(layout.fBlendSupportAllEquations) {}
 
-    Layout(int location, int binding, int index, int set, int builtin, bool originUpperLeft)
+    Layout(int location, int binding, int index, int set, int builtin, bool originUpperLeft,
+           bool overrideCoverage, bool blendSupportAllEquations)
     : fLocation(location)
     , fBinding(binding)
     , fIndex(index)
     , fSet(set)
     , fBuiltin(builtin)
-    , fOriginUpperLeft(originUpperLeft) {}
+    , fOriginUpperLeft(originUpperLeft)
+    , fOverrideCoverage(overrideCoverage)
+    , fBlendSupportAllEquations(blendSupportAllEquations) {}
 
     std::string description() const {
         std::string result;
@@ -59,6 +64,14 @@
             result += separator + "origin_upper_left";
             separator = ", ";
         }
+        if (fOverrideCoverage) {
+            result += separator + "override_coverage";
+            separator = ", ";
+        }
+        if (fBlendSupportAllEquations) {
+            result += separator + "blend_support_all_equations";
+            separator = ", ";
+        }
         if (result.length() > 0) {
             result = "layout (" + result + ")";
         }
@@ -66,11 +79,14 @@
     }
 
     bool operator==(const Layout& other) const {
-        return fLocation == other.fLocation &&
-               fBinding  == other.fBinding &&
-               fIndex    == other.fIndex &&
-               fSet      == other.fSet &&
-               fBuiltin  == other.fBuiltin;
+        return fLocation                 == other.fLocation &&
+               fBinding                  == other.fBinding &&
+               fIndex                    == other.fIndex &&
+               fSet                      == other.fSet &&
+               fBuiltin                  == other.fBuiltin &&
+               fOriginUpperLeft          == other.fOriginUpperLeft &&
+               fOverrideCoverage         == other.fOverrideCoverage &&
+               fBlendSupportAllEquations == other.fBlendSupportAllEquations;
     }
 
     bool operator!=(const Layout& other) const {
@@ -85,6 +101,8 @@
     int fSet;
     int fBuiltin;
     bool fOriginUpperLeft;
+    bool fOverrideCoverage;
+    bool fBlendSupportAllEquations;
 };
 
 } // namespace
diff --git a/src/sksl/ir/SkSLModifiersDeclaration.h b/src/sksl/ir/SkSLModifiersDeclaration.h
new file mode 100644
index 0000000..0066fab
--- /dev/null
+++ b/src/sksl/ir/SkSLModifiersDeclaration.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+ 
+#ifndef SKSL_MODIFIERDECLARATION
+#define SKSL_MODIFIERDECLARATION
+
+#include "SkSLProgramElement.h"
+#include "SkSLModifiers.h"
+
+namespace SkSL {
+
+/**
+ * A declaration that consists only of modifiers, e.g.:
+ *
+ * layout(blend_support_all_equations) out;
+ */
+struct ModifiersDeclaration : public ProgramElement {
+    ModifiersDeclaration(Modifiers modifiers)
+    : INHERITED(Position(), kModifiers_Kind)
+    , fModifiers(modifiers) {}
+
+    std::string description() const {
+        return fModifiers.description() + ";";
+    }
+
+    Modifiers fModifiers;
+
+    typedef ProgramElement INHERITED;
+};
+
+} // namespace
+
+#endif
diff --git a/src/sksl/ir/SkSLProgramElement.h b/src/sksl/ir/SkSLProgramElement.h
index 44fc340..2f1ce77 100644
--- a/src/sksl/ir/SkSLProgramElement.h
+++ b/src/sksl/ir/SkSLProgramElement.h
@@ -20,7 +20,8 @@
         kVar_Kind,
         kFunction_Kind,
         kInterfaceBlock_Kind,
-        kExtension_Kind
+        kExtension_Kind,
+        kModifiers_Kind
     };
 
     ProgramElement(Position position, Kind kind)
diff --git a/src/sksl/lex.sksl.c b/src/sksl/lex.sksl.c
index 4993fac..ed6bd6b 100644
--- a/src/sksl/lex.sksl.c
+++ b/src/sksl/lex.sksl.c
@@ -4,7 +4,7 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
-
+ 
 #line 3 "lex.sksl.c"
 
 #define  YY_INT_ALIGNED short int
@@ -14,7 +14,7 @@
 #define FLEX_SCANNER
 #define YY_FLEX_MAJOR_VERSION 2
 #define YY_FLEX_MINOR_VERSION 5
-#define YY_FLEX_SUBMINOR_VERSION 37
+#define YY_FLEX_SUBMINOR_VERSION 35
 #if YY_FLEX_SUBMINOR_VERSION > 0
 #define FLEX_BETA
 #endif
@@ -52,6 +52,7 @@
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -59,6 +60,7 @@
 typedef unsigned char flex_uint8_t; 
 typedef unsigned short int flex_uint16_t;
 typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
 
 /* Limits of integral types. */
 #ifndef INT8_MIN
@@ -89,8 +91,6 @@
 #define UINT32_MAX             (4294967295U)
 #endif
 
-#endif /* ! C99 */
-
 #endif /* ! FLEXINT_H */
 
 #ifdef __cplusplus
@@ -194,7 +194,7 @@
      */
     #define  YY_LESS_LINENO(n) \
             do { \
-                int yyl;\
+                yy_size_t yyl;\
                 for ( yyl = n; yyl < yyleng; ++yyl )\
                     if ( yytext[yyl] == '\n' )\
                         --yylineno;\
@@ -360,13 +360,13 @@
  */
 #define YY_DO_BEFORE_ACTION \
 	yyg->yytext_ptr = yy_bp; \
-	yyleng = (size_t) (yy_cp - yy_bp); \
+	yyleng = (yy_size_t) (yy_cp - yy_bp); \
 	yyg->yy_hold_char = *yy_cp; \
 	*yy_cp = '\0'; \
 	yyg->yy_c_buf_p = yy_cp;
 
-#define YY_NUM_RULES 82
-#define YY_END_OF_BUFFER 83
+#define YY_NUM_RULES 83
+#define YY_END_OF_BUFFER 84
 /* This struct is not used in this scanner,
    but its presence is necessary. */
 struct yy_trans_info
@@ -374,30 +374,31 @@
 	flex_int32_t yy_verify;
 	flex_int32_t yy_nxt;
 	};
-static yyconst flex_int16_t yy_accept[201] =
+static yyconst flex_int16_t yy_accept[204] =
     {   0,
-        0,    0,   83,   81,   80,   80,   54,   81,   29,   45,
-       50,   31,   32,   43,   41,   38,   42,   37,   44,    4,
-       56,   77,   61,   57,   60,   55,   35,   36,   49,   29,
-       29,   29,   29,   29,   29,   29,   29,   29,   29,   29,
-       29,   29,   29,   29,   29,   29,   33,   48,   34,   80,
-       59,   30,   29,   68,   53,   73,   66,   39,   64,   40,
-       65,    1,    0,   78,   67,    2,    4,    0,   46,   63,
-       58,   62,   47,   72,   52,   29,   29,   29,   11,   29,
-       29,   29,   29,   29,    7,   16,   29,   29,   29,   29,
-       29,   29,   29,   29,   29,   29,   29,   71,   51,   30,
+        0,    0,   84,   82,   81,   81,   55,   82,   30,   46,
+       51,   32,   33,   44,   42,   39,   43,   38,   45,    4,
+        4,   57,   78,   62,   58,   61,   56,   36,   37,   50,
+       30,   30,   30,   30,   30,   30,   30,   30,   30,   30,
+       30,   30,   30,   30,   30,   30,   30,   34,   49,   35,
+       81,   60,   31,   30,   69,   54,   74,   67,   40,   65,
+       41,   66,    1,    0,   79,   68,    2,    4,    0,    0,
+       47,   64,   59,   63,   48,   73,   53,   30,   30,   30,
+       12,   30,   30,   30,   30,   30,    8,   17,   30,   30,
+       30,   30,   30,   30,   30,   30,   30,   30,   30,   72,
 
-       76,    0,    0,    0,   78,    1,    0,    0,    3,   69,
-       70,   75,   29,   29,   29,   29,   29,   29,    9,   29,
-       29,   29,   29,   29,   29,   17,   29,   29,   29,   29,
-       29,   29,   74,    0,    1,   79,    0,    0,    2,   29,
-       29,   29,   29,    8,   29,   24,   29,   29,   29,   21,
-       29,   29,   29,   29,   29,    5,   29,   29,    0,    1,
-       12,   20,   29,   29,    6,   23,   18,   29,   29,   29,
-       29,   29,   29,   29,   10,   29,   29,   27,   29,   29,
-       29,   15,   26,   29,   29,   14,   22,   29,   29,   19,
-       13,   29,   29,   29,   28,   29,   29,   29,   25,    0
+       52,   31,   77,    0,    0,    0,   79,    1,    0,    0,
+        3,    5,   70,   71,   76,   30,   30,   30,   30,   30,
+       30,   10,   30,   30,   30,   30,   30,   30,   18,   30,
+       30,   30,   30,   30,   30,   75,    0,    1,   80,    0,
+        0,    2,   30,   30,   30,   30,    9,   30,   25,   30,
+       30,   30,   22,   30,   30,   30,   30,   30,    6,   30,
+       30,    0,    1,   13,   21,   30,   30,    7,   24,   19,
+       30,   30,   30,   30,   30,   30,   30,   11,   30,   30,
+       28,   30,   30,   30,   16,   27,   30,   30,   15,   23,
+       30,   30,   20,   14,   30,   30,   30,   29,   30,   30,
 
+       30,   26,    0
     } ;
 
 static yyconst flex_int32_t yy_ec[256] =
@@ -406,16 +407,16 @@
         1,    1,    2,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    2,    4,    1,    5,    6,    7,    8,    1,    9,
-       10,   11,   12,   13,   14,   15,   16,   17,   17,   17,
-       17,   17,   17,   17,   17,   17,   17,   18,   19,   20,
-       21,   22,   23,    1,    6,    6,    6,    6,   24,    6,
-        6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
-        6,    6,    6,    6,    6,    6,    6,    6,    6,    6,
-       25,    1,   26,   27,    6,    1,   28,   29,   30,   31,
+       10,   11,   12,   13,   14,   15,   16,   17,   18,   18,
+       18,   18,   18,   18,   18,   18,   18,   19,   20,   21,
+       22,   23,   24,    1,   25,   25,   25,   25,   26,   25,
+       25,   25,   25,   25,   25,   25,   25,   25,   25,   25,
+       25,   25,   25,   25,   25,   25,   25,   25,   25,   25,
+       27,    1,   28,   29,    6,    1,   30,   31,   32,   33,
 
-       32,   33,   34,   35,   36,    6,   37,   38,   39,   40,
-       41,   42,    6,   43,   44,   45,   46,   47,   48,    6,
-       49,    6,   50,   51,   52,    1,    1,    1,    1,    1,
+       34,   35,   36,   37,   38,   25,   39,   40,   41,   42,
+       43,   44,   25,   45,   46,   47,   48,   49,   50,   51,
+       52,   25,   53,   54,   55,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
@@ -432,152 +433,158 @@
         1,    1,    1,    1,    1
     } ;
 
-static yyconst flex_int32_t yy_meta[53] =
+static yyconst flex_int32_t yy_meta[56] =
     {   0,
         1,    1,    2,    1,    1,    3,    1,    1,    1,    1,
-        1,    1,    1,    1,    1,    1,    4,    1,    1,    1,
-        1,    1,    1,    3,    1,    1,    1,    3,    3,    3,
-        3,    3,    3,    3,    3,    3,    3,    3,    3,    3,
-        3,    3,    3,    3,    3,    3,    3,    3,    3,    1,
-        1,    1
+        1,    1,    1,    1,    1,    1,    4,    4,    1,    1,
+        1,    1,    1,    1,    5,    5,    1,    1,    1,    5,
+        5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
+        5,    5,    5,    5,    5,    5,    5,    5,    5,    5,
+        5,    5,    1,    1,    1
     } ;
 
-static yyconst flex_int16_t yy_base[206] =
+static yyconst flex_int16_t yy_base[210] =
     {   0,
-        0,    0,  238,  239,   51,   53,  216,    0,    0,  215,
-       49,  239,  239,  214,   46,  239,   45,  217,   52,   45,
-      239,  239,   44,  212,   50,  239,  239,  239,   53,  189,
-      190,   40,  192,   47,  193,   46,   50,  196,  186,  180,
-      182,  192,  178,  179,  181,  185,  239,   61,  239,   81,
-      239,    0,    0,  239,  198,  239,  239,  239,  239,  239,
-      239,   70,  207,    0,  239,   72,   75,   81,  196,  239,
-      239,  239,  195,  239,  194,  182,  173,  168,    0,  167,
-      172,  181,  165,  173,    0,  165,  156,  156,  172,  160,
-      156,  168,  154,  155,  151,  160,  159,  239,  173,    0,
+        0,    0,  272,  273,   54,   56,  249,    0,    0,  248,
+       52,  273,  273,  247,   49,  273,   48,   46,   56,   51,
+       58,  273,  273,   58,  246,   59,  273,  273,  273,   61,
+      222,  223,   48,  225,   57,  226,   53,   63,  229,  219,
+      213,  215,  225,  211,  212,  214,  218,  273,   67,  273,
+       96,  273,    0,    0,  273,  232,  273,  273,  273,  273,
+      273,  273,   86,  242,    0,  273,   90,   96,  111,    0,
+      230,  273,  273,  273,  229,  273,  228,  215,  206,  201,
+        0,  200,  205,  214,  198,  206,    0,  198,  188,  189,
+      205,  193,  189,  201,  187,  188,  184,  193,  192,  273,
 
-      239,   89,  182,  176,    0,   91,   97,  174,  173,  239,
-      239,  239,  161,   72,  158,  155,  142,  140,    0,  149,
-      137,  141,  139,  144,  147,    0,  148,  131,  130,  143,
-      141,  135,  239,  155,  154,  239,  107,  153,  152,  131,
-      122,  130,  137,    0,  132,    0,  121,  117,  115,    0,
-      114,  116,  122,  114,  126,    0,  114,  122,  136,  135,
-        0,    0,  111,  107,    0,    0,    0,  104,  109,  103,
-      102,  105,   99,  100,    0,   96,  110,    0,   98,   97,
-      102,    0,    0,   98,  102,    0,    0,   90,   79,    0,
-        0,   88,   73,   65,    0,   69,   53,   65,    0,  239,
+      207,    0,  273,  119,  217,  211,    0,   92,  126,  117,
+      124,    0,  273,  273,  273,  196,   99,  193,  190,  177,
+      175,    0,  184,  172,  176,  174,  179,  182,    0,  183,
+      166,  165,  178,  176,  170,  273,  130,  132,  273,  139,
+      137,  141,  170,  161,  169,  176,    0,  171,    0,  160,
+      156,  154,    0,  153,  155,  161,  153,  165,    0,  153,
+      161,  143,  145,    0,    0,  152,  148,    0,    0,    0,
+      145,  150,  144,  143,  146,  140,  141,    0,  137,  146,
+        0,  121,  108,  101,    0,    0,   91,   93,    0,    0,
+       85,   74,    0,    0,   83,   63,   54,    0,   58,   45,
 
-       58,  122,  124,  128,  132
+       31,    0,  273,  161,  164,  167,  172,  177,  179
     } ;
 
-static yyconst flex_int16_t yy_def[206] =
+static yyconst flex_int16_t yy_def[210] =
     {   0,
-      200,    1,  200,  200,  200,  200,  200,  201,  202,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  200,  200,  200,  200,
-      200,  203,  202,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  204,  205,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  200,  200,  203,
+      203,    1,  203,  203,  203,  203,  203,  204,  205,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  203,  203,  203,
+      203,  203,  206,  205,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  207,  208,  203,  203,  203,  203,  209,
+      203,  203,  203,  203,  203,  203,  203,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  203,
 
-      200,  200,  204,  204,  205,  200,  200,  200,  200,  200,
-      200,  200,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  200,  200,  200,  200,  200,  200,  200,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  200,  200,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,  202,
-      202,  202,  202,  202,  202,  202,  202,  202,  202,    0,
+      203,  206,  203,  203,  207,  207,  208,  203,  203,  203,
+      203,  209,  203,  203,  203,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  203,  203,  203,  203,  203,
+      203,  203,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  203,  203,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
+      205,  205,  205,  205,  205,  205,  205,  205,  205,  205,
 
-      200,  200,  200,  200,  200
+      205,  205,    0,  203,  203,  203,  203,  203,  203
     } ;
 
-static yyconst flex_int16_t yy_nxt[292] =
+static yyconst flex_int16_t yy_nxt[329] =
     {   0,
         4,    5,    6,    7,    8,    9,   10,   11,   12,   13,
        14,   15,   16,   17,   18,   19,   20,   21,   22,   23,
-       24,   25,   26,    9,   27,   28,   29,    9,   30,   31,
-       32,   33,   34,    9,   35,   36,    9,   37,   38,   39,
-       40,   41,   42,   43,   44,   45,    9,   46,    9,   47,
-       48,   49,   50,   50,   50,   50,   55,   58,   60,   66,
-       52,   67,   63,   69,   70,   61,   59,   64,   68,   56,
-       72,   73,   65,   74,   81,   78,   68,   87,   85,   75,
-       79,   98,   50,   50,   82,   86,   62,   83,  106,   66,
-       88,   67,  108,  102,  108,  107,  199,  109,   68,  198,
+       24,   25,   26,   27,    9,    9,   28,   29,   30,    9,
+       31,   32,   33,   34,   35,    9,   36,   37,    9,   38,
+       39,   40,   41,   42,   43,   44,   45,   46,    9,   47,
+        9,    9,   48,   49,   50,   51,   51,   51,   51,   56,
+       59,   61,   63,   63,  202,   67,   64,   68,   68,   62,
+       60,   65,   67,   57,   68,   68,   69,   66,   71,   72,
+       74,   75,   76,   69,   69,   80,   83,   87,  100,   77,
+       81,   69,   89,  201,   88,  200,   84,   51,   51,   85,
 
-      134,  102,  134,  107,  197,  135,   68,  106,  138,  196,
-      138,   99,  195,  139,  137,  141,  142,  194,  159,  193,
-      159,  192,  137,  160,   53,   53,  100,  100,  103,  103,
-      103,  103,  105,  191,  105,  105,  190,  189,  188,  187,
-      186,  185,  184,  183,  182,  181,  180,  179,  178,  177,
-      176,  160,  160,  175,  174,  173,  172,  171,  170,  169,
-      168,  167,  166,  165,  164,  163,  162,  161,  139,  139,
-      135,  135,  158,  157,  156,  155,  154,  153,  152,  151,
-      150,  149,  148,  147,  146,  145,  144,  143,  140,  109,
-      109,  136,  104,  133,  132,  131,  130,  129,  128,  127,
+      199,   70,   63,   63,  198,   90,  108,  108,  108,  108,
+       67,  104,   68,   68,  197,  109,  196,  140,  195,  104,
+      101,   69,  110,  109,  110,  140,  194,  111,  111,   69,
+      137,  193,  137,  111,  111,  138,  138,  141,  192,  141,
+      111,  111,  142,  142,  144,  145,  138,  138,  138,  138,
+      162,  191,  162,  142,  142,  163,  163,  142,  142,  163,
+      163,  163,  163,   53,  190,   53,   54,   54,   54,  102,
+      102,  102,  105,  105,  105,  105,  105,  107,  189,  107,
+      107,  107,  112,  112,  188,  187,  186,  185,  184,  183,
+      182,  181,  180,  179,  178,  177,  176,  175,  174,  173,
 
-      126,  125,  124,  123,  122,  121,  120,  119,  118,  117,
-      116,  115,  114,  113,  112,  111,  110,  104,  101,   97,
-       96,   95,   94,   93,   92,   91,   90,   89,   84,   80,
-       77,   76,   71,   62,   57,   54,   51,  200,    3,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200
+      172,  171,  170,  169,  168,  167,  166,  165,  164,  161,
+      160,  159,  158,  157,  156,  155,  154,  153,  152,  151,
+      150,  149,  148,  147,  146,  143,  139,  106,  136,  135,
+      134,  133,  132,  131,  130,  129,  128,  127,  126,  125,
+      124,  123,  122,  121,  120,  119,  118,  117,  116,  115,
+      114,  113,  106,  103,   99,   98,   97,   96,   95,   94,
+       93,   92,   91,   86,   82,   79,   78,   73,   58,   55,
+       52,  203,    3,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
 
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203
     } ;
 
-static yyconst flex_int16_t yy_chk[292] =
+static yyconst flex_int16_t yy_chk[329] =
     {   0,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
         1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
-        1,    1,    5,    5,    6,    6,   11,   15,   17,   20,
-      201,   20,   19,   23,   23,   17,   15,   19,   20,   11,
-       25,   25,   19,   29,   34,   32,   20,   37,   36,   29,
-       32,   48,   50,   50,   34,   36,   62,   34,   66,   67,
-       37,   67,   68,   62,   68,   66,  198,   68,   67,  197,
+        1,    1,    1,    1,    1,    5,    5,    6,    6,   11,
+       15,   17,   18,   18,  201,   20,   19,   20,   20,   17,
+       15,   19,   21,   11,   21,   21,   20,   19,   24,   24,
+       26,   26,   30,   21,   20,   33,   35,   37,   49,   30,
+       33,   21,   38,  200,   37,  199,   35,   51,   51,   35,
 
-      102,   62,  102,   66,  196,  102,   67,  106,  107,  194,
-      107,   48,  193,  107,  106,  114,  114,  192,  137,  189,
-      137,  188,  106,  137,  202,  202,  203,  203,  204,  204,
-      204,  204,  205,  185,  205,  205,  184,  181,  180,  179,
-      177,  176,  174,  173,  172,  171,  170,  169,  168,  164,
-      163,  160,  159,  158,  157,  155,  154,  153,  152,  151,
-      149,  148,  147,  145,  143,  142,  141,  140,  139,  138,
-      135,  134,  132,  131,  130,  129,  128,  127,  125,  124,
-      123,  122,  121,  120,  118,  117,  116,  115,  113,  109,
-      108,  104,  103,   99,   97,   96,   95,   94,   93,   92,
+      197,   20,   63,   63,  196,   38,   67,   67,  108,  108,
+       68,   63,   68,   68,  195,   67,  192,  108,  191,   63,
+       49,   68,   69,   67,   69,  108,  188,   69,   69,   68,
+      104,  187,  104,  110,  110,  104,  104,  109,  184,  109,
+      111,  111,  109,  109,  117,  117,  137,  137,  138,  138,
+      140,  183,  140,  141,  141,  140,  140,  142,  142,  162,
+      162,  163,  163,  204,  182,  204,  205,  205,  205,  206,
+      206,  206,  207,  207,  207,  207,  207,  208,  180,  208,
+      208,  208,  209,  209,  179,  177,  176,  175,  174,  173,
+      172,  171,  167,  166,  161,  160,  158,  157,  156,  155,
 
-       91,   90,   89,   88,   87,   86,   84,   83,   82,   81,
-       80,   78,   77,   76,   75,   73,   69,   63,   55,   46,
-       45,   44,   43,   42,   41,   40,   39,   38,   35,   33,
-       31,   30,   24,   18,   14,   10,    7,    3,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200,  200,  200,  200,  200,  200,  200,  200,  200,  200,
-      200
+      154,  152,  151,  150,  148,  146,  145,  144,  143,  135,
+      134,  133,  132,  131,  130,  128,  127,  126,  125,  124,
+      123,  121,  120,  119,  118,  116,  106,  105,  101,   99,
+       98,   97,   96,   95,   94,   93,   92,   91,   90,   89,
+       88,   86,   85,   84,   83,   82,   80,   79,   78,   77,
+       75,   71,   64,   56,   47,   46,   45,   44,   43,   42,
+       41,   40,   39,   36,   34,   32,   31,   25,   14,   10,
+        7,    3,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
 
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203,  203,  203,
+      203,  203,  203,  203,  203,  203,  203,  203
     } ;
 
 /* Table of booleans, true if rule could match eol. */
-static yyconst flex_int32_t yy_rule_can_match_eol[83] =
+static yyconst flex_int32_t yy_rule_can_match_eol[84] =
     {   0,
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 
-    1, 0, 0,     };
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    1, 1, 0, 0,     };
 
 /* The intent behind this definition is that it'll catch
  * any uses of REJECT which flex missed.
@@ -600,7 +607,7 @@
     
 */
 #define YY_NO_UNISTD_H 1
-#line 598 "lex.sksl.c"
+#line 605 "lex.sksl.c"
 
 #define INITIAL 0
 
@@ -685,10 +692,6 @@
 
 void skslset_lineno (int line_number ,yyscan_t yyscanner );
 
-int skslget_column  (yyscan_t yyscanner );
-
-void skslset_column (int column_no ,yyscan_t yyscanner );
-
 /* Macros after this point can all be overridden by user definitions in
  * section 1.
  */
@@ -731,7 +734,7 @@
 /* This used to be an fputs(), but since the string might contain NUL's,
  * we now use fwrite().
  */
-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
 #endif
 
 /* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
@@ -742,7 +745,7 @@
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		size_t n; \
+		yy_size_t n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -828,7 +831,7 @@
 #line 23 "sksl.flex"
 
 
-#line 826 "lex.sksl.c"
+#line 829 "lex.sksl.c"
 
 	if ( !yyg->yy_init )
 		{
@@ -881,13 +884,13 @@
 			while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 				{
 				yy_current_state = (int) yy_def[yy_current_state];
-				if ( yy_current_state >= 201 )
+				if ( yy_current_state >= 204 )
 					yy_c = yy_meta[(unsigned int) yy_c];
 				}
 			yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
 			++yy_cp;
 			}
-		while ( yy_current_state != 200 );
+		while ( yy_current_state != 203 );
 		yy_cp = yyg->yy_last_accepting_cpos;
 		yy_current_state = yyg->yy_last_accepting_state;
 
@@ -898,7 +901,7 @@
 
 		if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
 			{
-			int yyl;
+			yy_size_t yyl;
 			for ( yyl = 0; yyl < yyleng; ++yyl )
 				if ( yytext[yyl] == '\n' )
 					   
@@ -942,396 +945,401 @@
 case 5:
 YY_RULE_SETUP
 #line 33 "sksl.flex"
-{ return SkSL::Token::TRUE_LITERAL; }
+{ return SkSL::Token::INT_LITERAL; }
 	YY_BREAK
 case 6:
 YY_RULE_SETUP
 #line 35 "sksl.flex"
-{ return SkSL::Token::FALSE_LITERAL; }
+{ return SkSL::Token::TRUE_LITERAL; }
 	YY_BREAK
 case 7:
 YY_RULE_SETUP
 #line 37 "sksl.flex"
-{ return SkSL::Token::IF; }
+{ return SkSL::Token::FALSE_LITERAL; }
 	YY_BREAK
 case 8:
 YY_RULE_SETUP
 #line 39 "sksl.flex"
-{ return SkSL::Token::ELSE; }
+{ return SkSL::Token::IF; }
 	YY_BREAK
 case 9:
 YY_RULE_SETUP
 #line 41 "sksl.flex"
-{ return SkSL::Token::FOR; }
+{ return SkSL::Token::ELSE; }
 	YY_BREAK
 case 10:
 YY_RULE_SETUP
 #line 43 "sksl.flex"
-{ return SkSL::Token::WHILE; }
+{ return SkSL::Token::FOR; }
 	YY_BREAK
 case 11:
 YY_RULE_SETUP
 #line 45 "sksl.flex"
-{ return SkSL::Token::DO; }
+{ return SkSL::Token::WHILE; }
 	YY_BREAK
 case 12:
 YY_RULE_SETUP
 #line 47 "sksl.flex"
-{ return SkSL::Token::BREAK; }
+{ return SkSL::Token::DO; }
 	YY_BREAK
 case 13:
 YY_RULE_SETUP
 #line 49 "sksl.flex"
-{ return SkSL::Token::CONTINUE; }
+{ return SkSL::Token::BREAK; }
 	YY_BREAK
 case 14:
 YY_RULE_SETUP
 #line 51 "sksl.flex"
-{ return SkSL::Token::DISCARD; }
+{ return SkSL::Token::CONTINUE; }
 	YY_BREAK
 case 15:
 YY_RULE_SETUP
 #line 53 "sksl.flex"
-{ return SkSL::Token::RETURN; }
+{ return SkSL::Token::DISCARD; }
 	YY_BREAK
 case 16:
 YY_RULE_SETUP
 #line 55 "sksl.flex"
-{ return SkSL::Token::IN; }
+{ return SkSL::Token::RETURN; }
 	YY_BREAK
 case 17:
 YY_RULE_SETUP
 #line 57 "sksl.flex"
-{ return SkSL::Token::OUT; }
+{ return SkSL::Token::IN; }
 	YY_BREAK
 case 18:
 YY_RULE_SETUP
 #line 59 "sksl.flex"
-{ return SkSL::Token::INOUT; }
+{ return SkSL::Token::OUT; }
 	YY_BREAK
 case 19:
 YY_RULE_SETUP
 #line 61 "sksl.flex"
-{ return SkSL::Token::UNIFORM; }
+{ return SkSL::Token::INOUT; }
 	YY_BREAK
 case 20:
 YY_RULE_SETUP
 #line 63 "sksl.flex"
-{ return SkSL::Token::CONST; }
+{ return SkSL::Token::UNIFORM; }
 	YY_BREAK
 case 21:
 YY_RULE_SETUP
 #line 65 "sksl.flex"
-{ return SkSL::Token::LOWP; }
+{ return SkSL::Token::CONST; }
 	YY_BREAK
 case 22:
 YY_RULE_SETUP
 #line 67 "sksl.flex"
-{ return SkSL::Token::MEDIUMP; }
+{ return SkSL::Token::LOWP; }
 	YY_BREAK
 case 23:
 YY_RULE_SETUP
 #line 69 "sksl.flex"
-{ return SkSL::Token::HIGHP; }
+{ return SkSL::Token::MEDIUMP; }
 	YY_BREAK
 case 24:
 YY_RULE_SETUP
 #line 71 "sksl.flex"
-{ return SkSL::Token::FLAT; }
+{ return SkSL::Token::HIGHP; }
 	YY_BREAK
 case 25:
 YY_RULE_SETUP
 #line 73 "sksl.flex"
-{ return SkSL::Token::NOPERSPECTIVE; }
+{ return SkSL::Token::FLAT; }
 	YY_BREAK
 case 26:
 YY_RULE_SETUP
 #line 75 "sksl.flex"
-{ return SkSL::Token::STRUCT; }
+{ return SkSL::Token::NOPERSPECTIVE; }
 	YY_BREAK
 case 27:
 YY_RULE_SETUP
 #line 77 "sksl.flex"
-{ return SkSL::Token::LAYOUT; }
+{ return SkSL::Token::STRUCT; }
 	YY_BREAK
 case 28:
 YY_RULE_SETUP
 #line 79 "sksl.flex"
-{ return SkSL::Token::PRECISION; }
+{ return SkSL::Token::LAYOUT; }
 	YY_BREAK
 case 29:
 YY_RULE_SETUP
 #line 81 "sksl.flex"
-{ return SkSL::Token::IDENTIFIER; }
+{ return SkSL::Token::PRECISION; }
 	YY_BREAK
 case 30:
 YY_RULE_SETUP
 #line 83 "sksl.flex"
-{ return SkSL::Token::DIRECTIVE; }
+{ return SkSL::Token::IDENTIFIER; }
 	YY_BREAK
 case 31:
 YY_RULE_SETUP
 #line 85 "sksl.flex"
-{ return SkSL::Token::LPAREN; }
+{ return SkSL::Token::DIRECTIVE; }
 	YY_BREAK
 case 32:
 YY_RULE_SETUP
 #line 87 "sksl.flex"
-{ return SkSL::Token::RPAREN; }
+{ return SkSL::Token::LPAREN; }
 	YY_BREAK
 case 33:
 YY_RULE_SETUP
 #line 89 "sksl.flex"
-{ return SkSL::Token::LBRACE; }
+{ return SkSL::Token::RPAREN; }
 	YY_BREAK
 case 34:
 YY_RULE_SETUP
 #line 91 "sksl.flex"
-{ return SkSL::Token::RBRACE; }
+{ return SkSL::Token::LBRACE; }
 	YY_BREAK
 case 35:
 YY_RULE_SETUP
 #line 93 "sksl.flex"
-{ return SkSL::Token::LBRACKET; }
+{ return SkSL::Token::RBRACE; }
 	YY_BREAK
 case 36:
 YY_RULE_SETUP
 #line 95 "sksl.flex"
-{ return SkSL::Token::RBRACKET; }
+{ return SkSL::Token::LBRACKET; }
 	YY_BREAK
 case 37:
 YY_RULE_SETUP
 #line 97 "sksl.flex"
-{ return SkSL::Token::DOT; }
+{ return SkSL::Token::RBRACKET; }
 	YY_BREAK
 case 38:
 YY_RULE_SETUP
 #line 99 "sksl.flex"
-{ return SkSL::Token::COMMA; }
+{ return SkSL::Token::DOT; }
 	YY_BREAK
 case 39:
 YY_RULE_SETUP
 #line 101 "sksl.flex"
-{ return SkSL::Token::PLUSPLUS; }
+{ return SkSL::Token::COMMA; }
 	YY_BREAK
 case 40:
 YY_RULE_SETUP
 #line 103 "sksl.flex"
-{ return SkSL::Token::MINUSMINUS; }
+{ return SkSL::Token::PLUSPLUS; }
 	YY_BREAK
 case 41:
 YY_RULE_SETUP
 #line 105 "sksl.flex"
-{ return SkSL::Token::PLUS; }
+{ return SkSL::Token::MINUSMINUS; }
 	YY_BREAK
 case 42:
 YY_RULE_SETUP
 #line 107 "sksl.flex"
-{ return SkSL::Token::MINUS; }
+{ return SkSL::Token::PLUS; }
 	YY_BREAK
 case 43:
 YY_RULE_SETUP
 #line 109 "sksl.flex"
-{ return SkSL::Token::STAR; }
+{ return SkSL::Token::MINUS; }
 	YY_BREAK
 case 44:
 YY_RULE_SETUP
 #line 111 "sksl.flex"
-{ return SkSL::Token::SLASH; }
+{ return SkSL::Token::STAR; }
 	YY_BREAK
 case 45:
 YY_RULE_SETUP
 #line 113 "sksl.flex"
-{ return SkSL::Token::PERCENT; }
+{ return SkSL::Token::SLASH; }
 	YY_BREAK
 case 46:
 YY_RULE_SETUP
 #line 115 "sksl.flex"
-{ return SkSL::Token::SHL; }
+{ return SkSL::Token::PERCENT; }
 	YY_BREAK
 case 47:
 YY_RULE_SETUP
 #line 117 "sksl.flex"
-{ return SkSL::Token::SHR; }
+{ return SkSL::Token::SHL; }
 	YY_BREAK
 case 48:
 YY_RULE_SETUP
 #line 119 "sksl.flex"
-{ return SkSL::Token::BITWISEOR; }
+{ return SkSL::Token::SHR; }
 	YY_BREAK
 case 49:
 YY_RULE_SETUP
 #line 121 "sksl.flex"
-{ return SkSL::Token::BITWISEXOR; }
+{ return SkSL::Token::BITWISEOR; }
 	YY_BREAK
 case 50:
 YY_RULE_SETUP
 #line 123 "sksl.flex"
-{ return SkSL::Token::BITWISEAND; }
+{ return SkSL::Token::BITWISEXOR; }
 	YY_BREAK
 case 51:
 YY_RULE_SETUP
 #line 125 "sksl.flex"
-{ return SkSL::Token::LOGICALOR; }
+{ return SkSL::Token::BITWISEAND; }
 	YY_BREAK
 case 52:
 YY_RULE_SETUP
 #line 127 "sksl.flex"
-{ return SkSL::Token::LOGICALXOR; }
+{ return SkSL::Token::LOGICALOR; }
 	YY_BREAK
 case 53:
 YY_RULE_SETUP
 #line 129 "sksl.flex"
-{ return SkSL::Token::LOGICALAND; }
+{ return SkSL::Token::LOGICALXOR; }
 	YY_BREAK
 case 54:
 YY_RULE_SETUP
 #line 131 "sksl.flex"
-{ return SkSL::Token::NOT; }
+{ return SkSL::Token::LOGICALAND; }
 	YY_BREAK
 case 55:
 YY_RULE_SETUP
 #line 133 "sksl.flex"
-{ return SkSL::Token::QUESTION; }
+{ return SkSL::Token::NOT; }
 	YY_BREAK
 case 56:
 YY_RULE_SETUP
 #line 135 "sksl.flex"
-{ return SkSL::Token::COLON; }
+{ return SkSL::Token::QUESTION; }
 	YY_BREAK
 case 57:
 YY_RULE_SETUP
 #line 137 "sksl.flex"
-{ return SkSL::Token::EQ; }
+{ return SkSL::Token::COLON; }
 	YY_BREAK
 case 58:
 YY_RULE_SETUP
 #line 139 "sksl.flex"
-{ return SkSL::Token::EQEQ; }
+{ return SkSL::Token::EQ; }
 	YY_BREAK
 case 59:
 YY_RULE_SETUP
 #line 141 "sksl.flex"
-{ return SkSL::Token::NEQ; }
+{ return SkSL::Token::EQEQ; }
 	YY_BREAK
 case 60:
 YY_RULE_SETUP
 #line 143 "sksl.flex"
-{ return SkSL::Token::GT; }
+{ return SkSL::Token::NEQ; }
 	YY_BREAK
 case 61:
 YY_RULE_SETUP
 #line 145 "sksl.flex"
-{ return SkSL::Token::LT; }
+{ return SkSL::Token::GT; }
 	YY_BREAK
 case 62:
 YY_RULE_SETUP
 #line 147 "sksl.flex"
-{ return SkSL::Token::GTEQ; }
+{ return SkSL::Token::LT; }
 	YY_BREAK
 case 63:
 YY_RULE_SETUP
 #line 149 "sksl.flex"
-{ return SkSL::Token::LTEQ; }
+{ return SkSL::Token::GTEQ; }
 	YY_BREAK
 case 64:
 YY_RULE_SETUP
 #line 151 "sksl.flex"
-{ return SkSL::Token::PLUSEQ; }
+{ return SkSL::Token::LTEQ; }
 	YY_BREAK
 case 65:
 YY_RULE_SETUP
 #line 153 "sksl.flex"
-{ return SkSL::Token::MINUSEQ; }
+{ return SkSL::Token::PLUSEQ; }
 	YY_BREAK
 case 66:
 YY_RULE_SETUP
 #line 155 "sksl.flex"
-{ return SkSL::Token::STAREQ; }
+{ return SkSL::Token::MINUSEQ; }
 	YY_BREAK
 case 67:
 YY_RULE_SETUP
 #line 157 "sksl.flex"
-{ return SkSL::Token::SLASHEQ; }
+{ return SkSL::Token::STAREQ; }
 	YY_BREAK
 case 68:
 YY_RULE_SETUP
 #line 159 "sksl.flex"
-{ return SkSL::Token::PERCENTEQ; }
+{ return SkSL::Token::SLASHEQ; }
 	YY_BREAK
 case 69:
 YY_RULE_SETUP
 #line 161 "sksl.flex"
-{ return SkSL::Token::SHLEQ; }
+{ return SkSL::Token::PERCENTEQ; }
 	YY_BREAK
 case 70:
 YY_RULE_SETUP
 #line 163 "sksl.flex"
-{ return SkSL::Token::SHREQ; }
+{ return SkSL::Token::SHLEQ; }
 	YY_BREAK
 case 71:
 YY_RULE_SETUP
 #line 165 "sksl.flex"
-{ return SkSL::Token::BITWISEOREQ; }
+{ return SkSL::Token::SHREQ; }
 	YY_BREAK
 case 72:
 YY_RULE_SETUP
 #line 167 "sksl.flex"
-{ return SkSL::Token::BITWISEXOREQ; }
+{ return SkSL::Token::BITWISEOREQ; }
 	YY_BREAK
 case 73:
 YY_RULE_SETUP
 #line 169 "sksl.flex"
-{ return SkSL::Token::BITWISEANDEQ; }
+{ return SkSL::Token::BITWISEXOREQ; }
 	YY_BREAK
 case 74:
 YY_RULE_SETUP
 #line 171 "sksl.flex"
-{ return SkSL::Token::LOGICALOREQ; }
+{ return SkSL::Token::BITWISEANDEQ; }
 	YY_BREAK
 case 75:
 YY_RULE_SETUP
 #line 173 "sksl.flex"
-{ return SkSL::Token::LOGICALXOREQ; }
+{ return SkSL::Token::LOGICALOREQ; }
 	YY_BREAK
 case 76:
 YY_RULE_SETUP
 #line 175 "sksl.flex"
-{ return SkSL::Token::LOGICALANDEQ; }
+{ return SkSL::Token::LOGICALXOREQ; }
 	YY_BREAK
 case 77:
 YY_RULE_SETUP
 #line 177 "sksl.flex"
-{ return SkSL::Token::SEMICOLON; }
+{ return SkSL::Token::LOGICALANDEQ; }
 	YY_BREAK
 case 78:
 YY_RULE_SETUP
 #line 179 "sksl.flex"
-/* line comment */
+{ return SkSL::Token::SEMICOLON; }
 	YY_BREAK
 case 79:
-/* rule 79 can match eol */
 YY_RULE_SETUP
 #line 181 "sksl.flex"
-/* block comment */
+/* line comment */
 	YY_BREAK
 case 80:
 /* rule 80 can match eol */
 YY_RULE_SETUP
 #line 183 "sksl.flex"
-/* whitespace */
+/* block comment */
 	YY_BREAK
 case 81:
+/* rule 81 can match eol */
 YY_RULE_SETUP
 #line 185 "sksl.flex"
-{ return SkSL::Token::INVALID_TOKEN; }
+/* whitespace */
 	YY_BREAK
 case 82:
 YY_RULE_SETUP
 #line 187 "sksl.flex"
+{ return SkSL::Token::INVALID_TOKEN; }
+	YY_BREAK
+case 83:
+YY_RULE_SETUP
+#line 189 "sksl.flex"
 ECHO;
 	YY_BREAK
-#line 1329 "lex.sksl.c"
+#line 1337 "lex.sksl.c"
 case YY_STATE_EOF(INITIAL):
 	yyterminate();
 
@@ -1526,7 +1534,7 @@
 			{ /* Not enough room in the buffer - grow it. */
 
 			/* just a shorter name for the current buffer */
-			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
 
 			int yy_c_buf_p_offset =
 				(int) (yyg->yy_c_buf_p - b->yy_ch_buf);
@@ -1626,7 +1634,7 @@
 		while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 			{
 			yy_current_state = (int) yy_def[yy_current_state];
-			if ( yy_current_state >= 201 )
+			if ( yy_current_state >= 204 )
 				yy_c = yy_meta[(unsigned int) yy_c];
 			}
 		yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
@@ -1655,13 +1663,12 @@
 	while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
 		{
 		yy_current_state = (int) yy_def[yy_current_state];
-		if ( yy_current_state >= 201 )
+		if ( yy_current_state >= 204 )
 			yy_c = yy_meta[(unsigned int) yy_c];
 		}
 	yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
-	yy_is_jam = (yy_current_state == 200);
+	yy_is_jam = (yy_current_state == 203);
 
-	(void)yyg;
 	return yy_is_jam ? 0 : yy_current_state;
 }
 
@@ -1756,7 +1763,7 @@
 				case EOB_ACT_END_OF_FILE:
 					{
 					if ( skslwrap(yyscanner ) )
-						return EOF;
+						return 0;
 
 					if ( ! yyg->yy_did_buffer_switch_on_eof )
 						YY_NEW_FILE;
@@ -2112,8 +2119,8 @@
 
 /** Setup the input buffer state to scan the given bytes. The next call to sksllex() will
  * scan from a @e copy of @a bytes.
- * @param yybytes the byte buffer to scan
- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
  * @param yyscanner The scanner object.
  * @return the newly allocated buffer state object.
  */
@@ -2121,8 +2128,7 @@
 {
 	YY_BUFFER_STATE b;
 	char *buf;
-	yy_size_t n;
-	int i;
+	yy_size_t n, i;
     
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = _yybytes_len + 2;
@@ -2268,7 +2274,7 @@
 
         /* lineno is only valid if an input buffer exists. */
         if (! YY_CURRENT_BUFFER )
-           YY_FATAL_ERROR( "skslset_lineno called with no buffer" );
+           yy_fatal_error( "skslset_lineno called with no buffer" , yyscanner); 
     
     yylineno = line_number;
 }
@@ -2283,7 +2289,7 @@
 
         /* column is only valid if an input buffer exists. */
         if (! YY_CURRENT_BUFFER )
-           YY_FATAL_ERROR( "skslset_column called with no buffer" );
+           yy_fatal_error( "skslset_column called with no buffer" , yyscanner); 
     
     yycolumn = column_no;
 }
@@ -2495,7 +2501,7 @@
 
 #define YYTABLES_NAME "yytables"
 
-#line 187 "sksl.flex"
+#line 189 "sksl.flex"
 
 
 
diff --git a/src/sksl/sksl.flex b/src/sksl/sksl.flex
index 67b48e9..70a7718 100644
--- a/src/sksl/sksl.flex
+++ b/src/sksl/sksl.flex
@@ -30,6 +30,8 @@
 
 {DIGIT}+ { return SkSL::Token::INT_LITERAL; }
 
+"0x"[0-9a-zA-Z]+ { return SkSL::Token::INT_LITERAL; }
+
 true { return SkSL::Token::TRUE_LITERAL; }
 
 false { return SkSL::Token::FALSE_LITERAL; }
diff --git a/src/sksl/sksl.include b/src/sksl/sksl.include
index bf35f22..2cb0116 100644
--- a/src/sksl/sksl.include
+++ b/src/sksl/sksl.include
@@ -244,6 +244,8 @@
 $gvec4 texture($gsampler1D sampler, float P);
 $gvec4 texture($gsampler1D sampler, float P, float bias);
 $gvec4 texture($gsampler2D sampler, vec2 P);
+vec4 texture(samplerExternalOES sampler, vec2 P, float bias);
+vec4 texture(samplerExternalOES sampler, vec2 P);
 $gvec4 texture($gsampler2D sampler, vec2 P, float bias);
 $gvec4 texture($gsampler3D sampler, vec3 P);
 $gvec4 texture($gsampler3D sampler, vec3 P, float bias);
@@ -464,6 +466,7 @@
 vec4 texture1DProjLod(sampler1D sampler, vec2 coord, float lod);
 vec4 texture1DProjLod(sampler1D sampler, vec4 coord, float lod);
 vec4 texture2D(sampler2D sampler, vec2 coord);
+vec4 texture2D(samplerExternalOES sampler, vec2 coord);
 vec4 texture2D(sampler2D sampler, vec2 coord, float bias);
 vec4 texture2DProj(sampler2D sampler, vec3 coord);
 vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);
diff --git a/src/sksl/sksl_frag.include b/src/sksl/sksl_frag.include
index b4047fe..98ea65f 100644
--- a/src/sksl/sksl_frag.include
+++ b/src/sksl/sksl_frag.include
@@ -4,4 +4,16 @@
 
 layout(builtin=15) in vec4 gl_FragCoord;
 
+// 9999 is a temporary value that causes us to ignore these declarations beyond
+// adding them to the symbol table. This works fine in GLSL (where they do not
+// require any further handling) but will fail in SPIR-V. We'll have a better
+// solution for this soon.
+layout(builtin=9999) vec4 gl_LastFragData[1];
+layout(builtin=9999) vec4 gl_LastFragColor;
+layout(builtin=9999) vec4 gl_LastFragColorARM;
+layout(builtin=9999) int gl_SampleMaskIn[];
+layout(builtin=9999) out int gl_SampleMask[];
+
+layout(location=0,index=0,builtin=10001) out vec4 sk_FragColor;
+
 )
\ No newline at end of file
diff --git a/tests/SkSLErrorTest.cpp b/tests/SkSLErrorTest.cpp
index 75fa20d..400ab6d 100644
--- a/tests/SkSLErrorTest.cpp
+++ b/tests/SkSLErrorTest.cpp
@@ -263,8 +263,8 @@
                  "void main() { int x = 2[0]; }",
                  "error: 1: expected array, but found 'int'\n1 error\n");
     test_failure(r,
-                 "void main() { vec2 x = vec2(0); int y = x[0]; }",
-                 "error: 1: expected array, but found 'vec2'\n1 error\n");
+                 "void main() { vec2 x = vec2(0); int y = x[0][0]; }",
+                 "error: 1: expected array, but found 'float'\n1 error\n");
 }
 
 DEF_TEST(SkSLTernaryMismatch, r) {
diff --git a/tests/SkSLGLSLTest.cpp b/tests/SkSLGLSLTest.cpp
index 3906f67..af9fd83 100644
--- a/tests/SkSLGLSLTest.cpp
+++ b/tests/SkSLGLSLTest.cpp
@@ -26,46 +26,52 @@
     }
 }
 
+static SkSL::GLCaps default_caps() {
+    return { 
+             400, 
+             SkSL::GLCaps::kGL_Standard,
+             false, // isCoreProfile
+             false, // usesPrecisionModifiers;
+             false, // mustDeclareFragmentShaderOutput
+             true   // canUseMinAndAbsTogether
+           };
+}
+
 DEF_TEST(SkSLHelloWorld, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
-         "out vec4 fragColor; void main() { fragColor = vec4(0.75); }",
-         caps,
+         "void main() { sk_FragColor = vec4(0.75); }",
+         default_caps(),
          "#version 400\n"
-         "out vec4 fragColor;\n"
          "void main() {\n"
-         "    fragColor = vec4(0.75);\n"
+         "    gl_FragColor = vec4(0.75);\n"
          "}\n");
 }
 
 DEF_TEST(SkSLControl, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
-         "out vec4 fragColor;"
          "void main() {"
-         "if (1 + 2 + 3 > 5) { fragColor = vec4(0.75); } else { discard; }"
+         "if (1 + 2 + 3 > 5) { sk_FragColor = vec4(0.75); } else { discard; }"
          "int i = 0;"
-         "while (i < 10) fragColor *= 0.5;"
-         "do { fragColor += 0.01; } while (fragColor.x < 0.7);"
+         "while (i < 10) sk_FragColor *= 0.5;"
+         "do { sk_FragColor += 0.01; } while (sk_FragColor.x < 0.7);"
          "for (int i = 0; i < 10; i++) {"
          "if (i % 0 == 1) break; else continue;"
          "}"
          "return;"
          "}",
-         caps,
+         default_caps(),
          "#version 400\n"
-         "out vec4 fragColor;\n"
          "void main() {\n"
          "    if ((1 + 2) + 3 > 5) {\n"
-         "        fragColor = vec4(0.75);\n"
+         "        gl_FragColor = vec4(0.75);\n"
          "    } else {\n"
          "        discard;\n"
          "    }\n"
          "    int i = 0;\n"
-         "    while (i < 10) fragColor *= 0.5;\n"
+         "    while (i < 10) gl_FragColor *= 0.5;\n"
          "    do {\n"
-         "        fragColor += 0.01;\n"
-         "    } while (fragColor.x < 0.7);\n"
+         "        gl_FragColor += 0.01;\n"
+         "    } while (gl_FragColor.x < 0.7);\n"
          "    for (int i = 0;i < 10; i++) {\n"
          "        if (i % 0 == 1) break; else continue;\n"
          "    }\n"
@@ -74,34 +80,30 @@
 }
 
 DEF_TEST(SkSLFunctions, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
-         "out vec4 fragColor;"
          "float foo(float v[2]) { return v[0] * v[1]; }"
          "void bar(inout float x) { float y[2], z; y[0] = x; y[1] = x * 2; z = foo(y); x = z; }"
-         "void main() { float x = 10; bar(x); fragColor = vec4(x); }",
-         caps,
+         "void main() { float x = 10; bar(x); sk_FragColor = vec4(x); }",
+         default_caps(),
          "#version 400\n"
-         "out vec4 fragColor;\n"
-         "float foo(in float[2] v) {\n"
+         "float foo(in float v[2]) {\n"
          "    return v[0] * v[1];\n"
          "}\n"
          "void bar(inout float x) {\n"
          "    float y[2], z;\n"
          "    y[0] = x;\n"
-         "    y[1] = x * 2;\n"
+         "    y[1] = x * 2.0;\n"
          "    z = foo(y);\n"
          "    x = z;\n"
          "}\n"
          "void main() {\n"
-         "    float x = 10;\n"
+         "    float x = 10.0;\n"
          "    bar(x);\n"
-         "    fragColor = vec4(x);\n"
+         "    gl_FragColor = vec4(x);\n"
          "}\n");
 }
 
 DEF_TEST(SkSLOperators, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
          "void main() {"
          "float x = 1, y = 2;"
@@ -123,17 +125,17 @@
          "z <<= 4;"
          "z %= 5;"
          "}",
-         caps,
+         default_caps(),
          "#version 400\n"
          "void main() {\n"
-         "    float x = 1, y = 2;\n"
+         "    float x = 1.0, y = 2.0;\n"
          "    int z = 3;\n"
          "    x = x + ((y * float(z)) * x) * (y - float(z));\n"
          "    y = (x / y) / float(z);\n"
          "    z = (((z / 2) % 3 << 4) >> 2) << 1;\n"
-         "    bool b = x > 4 == x < 2 || (2 >= 5 && y <= float(z)) && 12 != 11;\n"
-         "    x += 12;\n"
-         "    x -= 12;\n"
+         "    bool b = x > 4.0 == x < 2.0 || (2 >= 5 && y <= float(z)) && 12 != 11;\n"
+         "    x += 12.0;\n"
+         "    x -= 12.0;\n"
          "    x *= (y /= float(z = 10));\n"
          "    b ||= false;\n"
          "    b &&= true;\n"
@@ -148,7 +150,6 @@
 }
 
 DEF_TEST(SkSLMatrices, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
          "void main() {"
          "mat2x4 x = mat2x4(1);"
@@ -157,19 +158,18 @@
          "vec3 v1 = mat3(1) * vec3(1);"
          "vec3 v2 = vec3(1) * mat3(1);"
          "}",
-         caps,
+         default_caps(),
          "#version 400\n"
          "void main() {\n"
-         "    mat2x4 x = mat2x4(1);\n"
-         "    mat3x2 y = mat3x2(1, 0, 0, 1, vec2(2, 2));\n"
+         "    mat2x4 x = mat2x4(1.0);\n"
+         "    mat3x2 y = mat3x2(1.0, 0.0, 0.0, 1.0, vec2(2.0, 2.0));\n"
          "    mat3x4 z = x * y;\n"
-         "    vec3 v1 = mat3(1) * vec3(1);\n"
-         "    vec3 v2 = vec3(1) * mat3(1);\n"
+         "    vec3 v1 = mat3(1.0) * vec3(1.0);\n"
+         "    vec3 v2 = vec3(1.0) * mat3(1.0);\n"
          "}\n");
 }
 
 DEF_TEST(SkSLInterfaceBlock, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
          "uniform testBlock {"
          "float x;"
@@ -179,12 +179,12 @@
          "};"
          "void main() {"
          "}",
-         caps,
+         default_caps(),
          "#version 400\n"
          "uniform testBlock {\n"
          "    float x;\n"
          "    float[2] y;\n"
-         "    layout (binding = 12)mat3x2 z;\n"
+         "    layout (binding = 12) mat3x2 z;\n"
          "    bool w;\n"
          "};\n"
          "void main() {\n"
@@ -192,7 +192,6 @@
 }
 
 DEF_TEST(SkSLStructs, r) {
-    SkSL::GLCaps caps = { 400, SkSL::GLCaps::kGL_Standard };
     test(r,
          "struct A {"
          "int x;"
@@ -207,7 +206,7 @@
          "B b1, b2, b3;"
          "void main() {"
          "}",
-         caps,
+         default_caps(),
          "#version 400\n"
          "struct A {\n"
          "    int x;\n"
@@ -218,10 +217,149 @@
          "struct B {\n"
          "    float x;\n"
          "    float[2] y;\n"
-         "    layout (binding = 1)A z;\n"
+         "    layout (binding = 1) A z;\n"
          "}\n"
          " b1, b2, b3;\n"
          "void main() {\n"
          "}\n");
+}
 
+DEF_TEST(SkSLVersion, r) {
+    SkSL::GLCaps caps = default_caps();
+    caps.fVersion = 450;
+    caps.fIsCoreProfile = true;
+    test(r,
+         "in float test; void main() { sk_FragColor = vec4(0.75); }",
+         caps,
+         "#version 450 core\n"
+         "in float test;\n"
+         "void main() {\n"
+         "    gl_FragColor = vec4(0.75);\n"
+         "}\n");
+    caps.fVersion = 110;
+    caps.fIsCoreProfile = false;
+    test(r,
+         "in float test; void main() { sk_FragColor = vec4(0.75); }",
+         caps,
+         "#version 110\n"
+         "varying float test;\n"
+         "void main() {\n"
+         "    gl_FragColor = vec4(0.75);\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLDeclareOutput, r) {
+    SkSL::GLCaps caps = default_caps();    
+    caps.fMustDeclareFragmentShaderOutput = true;
+    test(r,
+         "void main() { sk_FragColor = vec4(0.75); }",
+         caps,
+         "#version 400\n"
+         "out vec4 sk_FragColor;\n"
+         "void main() {\n"
+         "    sk_FragColor = vec4(0.75);\n"
+         "}\n");    
+}
+
+DEF_TEST(SkSLUsesPrecisionModifiers, r) {
+    SkSL::GLCaps caps = default_caps();
+    test(r,
+         "void main() { float x = 0.75; highp float y = 1; }",
+         caps,
+         "#version 400\n"
+         "void main() {\n"
+         "    float x = 0.75;\n"
+         "    float y = 1.0;\n"
+         "}\n");    
+    caps.fStandard = SkSL::GLCaps::kGLES_Standard;
+    caps.fUsesPrecisionModifiers = true;
+    test(r,
+         "void main() { float x = 0.75; highp float y = 1; }",
+         caps,
+         "#version 400 es\n"
+         "precision mediump float;\n"
+         "void main() {\n"
+         "    float x = 0.75;\n"
+         "    highp float y = 1.0;\n"
+         "}\n");    
+}
+
+DEF_TEST(SkSLMinAbs, r) {
+    test(r,
+         "void main() {"
+         "float x = -5;"
+         "x = min(abs(x), 6);"
+         "}",
+         default_caps(),
+         "#version 400\n"
+         "void main() {\n"
+         "    float x = -5.0;\n"
+         "    x = min(abs(x), 6.0);\n"
+         "}\n");
+
+    SkSL::GLCaps caps = default_caps();
+    caps.fCanUseMinAndAbsTogether = false;
+    test(r,
+         "void main() {"
+         "float x = -5.0;"
+         "x = min(abs(x), 6.0);"
+         "}",
+         caps,
+         "#version 400\n"
+         "void main() {\n"
+         "    float minAbsHackVar0;\n"
+         "    float x = -5.0;\n"
+         "    x = (abs(x) > (minAbsHackVar0 = 6.0) ? minAbsHackVar0 : abs(x));\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLModifiersDeclaration, r) {
+    test(r,
+         "layout(blend_support_all_equations) out;"
+         "void main() { }",
+         default_caps(),
+         "#version 400\n"
+         "layout (blend_support_all_equations) out ;\n"
+         "void main() {\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLHex, r) {
+    test(r,
+         "void main() {"
+         "int i1 = 0x0;"
+         "int i2 = 0x1234abcd;"
+         "int i3 = 0x7fffffff;"
+         "int i4 = 0xffffffff;"
+         "int i5 = -0xbeef;"
+         "uint u1 = 0x0;"
+         "uint u2 = 0x1234abcd;"
+         "uint u3 = 0x7fffffff;"
+         "uint u4 = 0xffffffff;"
+         "}",
+         default_caps(),
+         "#version 400\n"
+         "void main() {\n"
+         "    int i1 = 0;\n"
+         "    int i2 = 305441741;\n"
+         "    int i3 = 2147483647;\n"
+         "    int i4 = -1;\n"
+         "    int i5 = -48879;\n"
+         "    uint u1 = 0u;\n"
+         "    uint u2 = 305441741u;\n"
+         "    uint u3 = 2147483647u;\n"
+         "    uint u4 = 4294967295u;\n"
+         "}\n");
+}
+
+DEF_TEST(SkSLArrayConstructors, r) {
+    test(r,
+         "float test1[] = float[](1, 2, 3, 4);"
+         "vec2 test2[] = vec2[](vec2(1, 2), vec2(3, 4));"
+         "mat4 test3[] = mat4[]();",
+         default_caps(),
+         "#version 400\n"
+         "float test1[] = float[](1.0, 2.0, 3.0, 4.0);\n"
+         "vec2 test2[] = vec2[](vec2(1.0, 2.0), vec2(3.0, 4.0));\n"
+         "mat4 test3[] = mat4[]();\n");
 }