Add test of off-kilter matrix constructors.

This exposes a bug in the Metal code generator which will be resolved
in a followup CL.

Change-Id: If073835dbee474ea9a805eb92b42dc1fca2afbd0
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/448378
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
diff --git a/gn/sksl_tests.gni b/gn/sksl_tests.gni
index 7a1194f..68cde03 100644
--- a/gn/sksl_tests.gni
+++ b/gn/sksl_tests.gni
@@ -332,6 +332,8 @@
   "/sksl/shared/InterfaceBlockNamedArray.sksl",
   "/sksl/shared/Matrices.sksl",
   "/sksl/shared/MatricesNonsquare.sksl",
+  "/sksl/shared/MatrixConstructorsES2.sksl",
+  "/sksl/shared/MatrixConstructorsES3.sksl",
   "/sksl/shared/MatrixEquality.sksl",
   "/sksl/shared/MatrixScalarSplat.sksl",
   "/sksl/shared/MatrixToVectorCast.sksl",
diff --git a/resources/sksl/shared/MatrixConstructorsES2.sksl b/resources/sksl/shared/MatrixConstructorsES2.sksl
new file mode 100644
index 0000000..e2efde4
--- /dev/null
+++ b/resources/sksl/shared/MatrixConstructorsES2.sksl
@@ -0,0 +1,16 @@
+uniform half4 colorGreen, colorRed;
+uniform float2x2 testMatrix2x2;  // equals (1, 2, 3, 4)
+
+half4 main(float2 coords) {
+    float4 f4 = float4(testMatrix2x2);
+
+    // These matrices are intentionally assembled off-kilter; the vectors shouldn't line up with the
+    // natural matrix stride. Metal and SPIR-V will need to reorder the data to make it fit.
+    bool ok = float2x2(f4.xyz, 4)                       == float2x2(1, 2, 3, 4);
+    ok = ok && float3x3(f4.xy, f4.zw, f4.xyzw, f4.x)    == float3x3(1, 2, 3, 4, 1, 2, 3, 4, 1);
+    ok = ok && float4x4(f4.xy, f4.zwxy, f4.zwx, f4.yz, f4.wxyz, f4.w) == float4x4(1, 2, 3, 4,
+                                                                                  1, 2, 3, 4,
+                                                                                  1, 2, 3, 4,
+                                                                                  1, 2, 3, 4);
+    return ok ? colorGreen : colorRed;
+}
diff --git a/resources/sksl/shared/MatrixConstructorsES3.sksl b/resources/sksl/shared/MatrixConstructorsES3.sksl
new file mode 100644
index 0000000..7a7c0f9
--- /dev/null
+++ b/resources/sksl/shared/MatrixConstructorsES3.sksl
@@ -0,0 +1,18 @@
+uniform half4 colorGreen, colorRed;
+uniform float2x2 testMatrix2x2;  // equals (1, 2, 3, 4)
+
+half4 main(float2 coords) {
+    float4 f4 = float4(testMatrix2x2);
+
+    // These matrices are intentionally assembled off-kilter; the vectors shouldn't line up with the
+    // natural matrix stride. Metal and SPIR-V will need to reorder the data to make it fit.
+    bool ok =  float2x3(f4.xyzw, f4.xy)                 == float2x3(1, 2, 3, 4, 1, 2);
+    ok = ok && float2x4(f4.xyz, f4.wxyz, f4.w)          == float2x4(1, 2, 3, 4, 1, 2, 3, 4);
+    ok = ok && float3x3(f4.xy, f4.zw, f4.xyzw, f4.x)    == float3x3(1, 2, 3, 4, 1, 2, 3, 4, 1);
+    ok = ok && float4x2(f4.xyz, f4.wxyz, f4.w)          == float4x2(1, 2, 3, 4, 1, 2, 3, 4);
+    ok = ok && float4x3(f4.x, f4.yzwx, f4.yzwx, f4.yzw) == float4x3(1, 2, 3,
+                                                                    4, 1, 2,
+                                                                    3, 4, 1,
+                                                                    2, 3, 4);
+    return ok ? colorGreen : colorRed;
+}
diff --git a/tests/sksl/shared/MatrixConstructorsES2.asm.frag b/tests/sksl/shared/MatrixConstructorsES2.asm.frag
new file mode 100644
index 0000000..1d3e9ab
--- /dev/null
+++ b/tests/sksl/shared/MatrixConstructorsES2.asm.frag
@@ -0,0 +1,241 @@
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_FragColor %sk_Clockwise
+OpExecutionMode %_entrypoint_v OriginUpperLeft
+OpName %sk_FragColor "sk_FragColor"
+OpName %sk_Clockwise "sk_Clockwise"
+OpName %_UniformBuffer "_UniformBuffer"
+OpMemberName %_UniformBuffer 0 "colorGreen"
+OpMemberName %_UniformBuffer 1 "colorRed"
+OpMemberName %_UniformBuffer 2 "testMatrix2x2"
+OpName %_entrypoint_v "_entrypoint_v"
+OpName %main "main"
+OpName %f4 "f4"
+OpName %ok "ok"
+OpDecorate %sk_FragColor RelaxedPrecision
+OpDecorate %sk_FragColor Location 0
+OpDecorate %sk_FragColor Index 0
+OpDecorate %sk_Clockwise BuiltIn FrontFacing
+OpMemberDecorate %_UniformBuffer 0 Offset 0
+OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
+OpMemberDecorate %_UniformBuffer 1 Offset 16
+OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
+OpMemberDecorate %_UniformBuffer 2 Offset 32
+OpMemberDecorate %_UniformBuffer 2 ColMajor
+OpMemberDecorate %_UniformBuffer 2 MatrixStride 16
+OpDecorate %_UniformBuffer Block
+OpDecorate %10 Binding 0
+OpDecorate %10 DescriptorSet 0
+OpDecorate %68 RelaxedPrecision
+OpDecorate %111 RelaxedPrecision
+OpDecorate %173 RelaxedPrecision
+OpDecorate %181 RelaxedPrecision
+OpDecorate %184 RelaxedPrecision
+OpDecorate %185 RelaxedPrecision
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%sk_FragColor = OpVariable %_ptr_Output_v4float Output
+%bool = OpTypeBool
+%_ptr_Input_bool = OpTypePointer Input %bool
+%sk_Clockwise = OpVariable %_ptr_Input_bool Input
+%v2float = OpTypeVector %float 2
+%mat2v2float = OpTypeMatrix %v2float 2
+%_UniformBuffer = OpTypeStruct %v4float %v4float %mat2v2float
+%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
+%10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
+%void = OpTypeVoid
+%17 = OpTypeFunction %void
+%float_0 = OpConstant %float 0
+%20 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%24 = OpTypeFunction %v4float %_ptr_Function_v2float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Uniform_mat2v2float = OpTypePointer Uniform %mat2v2float
+%int = OpTypeInt 32 1
+%int_2 = OpConstant %int 2
+%_ptr_Function_bool = OpTypePointer Function %bool
+%v3float = OpTypeVector %float 3
+%float_4 = OpConstant %float 4
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%float_3 = OpConstant %float 3
+%v2bool = OpTypeVector %bool 2
+%false = OpConstantFalse %bool
+%mat3v3float = OpTypeMatrix %v3float 3
+%v3bool = OpTypeVector %bool 3
+%mat4v4float = OpTypeMatrix %v4float 4
+%v4bool = OpTypeVector %bool 4
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%_entrypoint_v = OpFunction %void None %17
+%18 = OpLabel
+%21 = OpVariable %_ptr_Function_v2float Function
+OpStore %21 %20
+%23 = OpFunctionCall %v4float %main %21
+OpStore %sk_FragColor %23
+OpReturn
+OpFunctionEnd
+%main = OpFunction %v4float None %24
+%25 = OpFunctionParameter %_ptr_Function_v2float
+%26 = OpLabel
+%f4 = OpVariable %_ptr_Function_v4float Function
+%ok = OpVariable %_ptr_Function_bool Function
+%174 = OpVariable %_ptr_Function_v4float Function
+%29 = OpAccessChain %_ptr_Uniform_mat2v2float %10 %int_2
+%33 = OpLoad %mat2v2float %29
+%34 = OpCompositeExtract %float %33 0 0
+%35 = OpCompositeExtract %float %33 0 1
+%36 = OpCompositeExtract %float %33 1 0
+%37 = OpCompositeExtract %float %33 1 1
+%38 = OpCompositeConstruct %v4float %34 %35 %36 %37
+OpStore %f4 %38
+%41 = OpLoad %v4float %f4
+%42 = OpVectorShuffle %v3float %41 %41 0 1 2
+%45 = OpCompositeExtract %float %42 0
+%46 = OpCompositeExtract %float %42 1
+%47 = OpCompositeConstruct %v2float %45 %46
+%48 = OpCompositeExtract %float %42 2
+%49 = OpCompositeConstruct %v2float %48 %float_4
+%50 = OpCompositeConstruct %mat2v2float %47 %49
+%54 = OpCompositeConstruct %v2float %float_1 %float_2
+%55 = OpCompositeConstruct %v2float %float_3 %float_4
+%56 = OpCompositeConstruct %mat2v2float %54 %55
+%58 = OpCompositeExtract %v2float %50 0
+%59 = OpCompositeExtract %v2float %56 0
+%60 = OpFOrdEqual %v2bool %58 %59
+%61 = OpAll %bool %60
+%62 = OpCompositeExtract %v2float %50 1
+%63 = OpCompositeExtract %v2float %56 1
+%64 = OpFOrdEqual %v2bool %62 %63
+%65 = OpAll %bool %64
+%66 = OpLogicalAnd %bool %61 %65
+OpStore %ok %66
+%68 = OpLoad %bool %ok
+OpSelectionMerge %70 None
+OpBranchConditional %68 %69 %70
+%69 = OpLabel
+%71 = OpLoad %v4float %f4
+%72 = OpVectorShuffle %v2float %71 %71 0 1
+%73 = OpLoad %v4float %f4
+%74 = OpVectorShuffle %v2float %73 %73 2 3
+%75 = OpLoad %v4float %f4
+%76 = OpLoad %v4float %f4
+%77 = OpCompositeExtract %float %76 0
+%78 = OpCompositeExtract %float %72 0
+%79 = OpCompositeExtract %float %72 1
+%80 = OpCompositeExtract %float %74 0
+%81 = OpCompositeConstruct %v3float %78 %79 %80
+%82 = OpCompositeExtract %float %74 1
+%83 = OpCompositeExtract %float %75 0
+%84 = OpCompositeExtract %float %75 1
+%85 = OpCompositeConstruct %v3float %82 %83 %84
+%86 = OpCompositeExtract %float %75 2
+%87 = OpCompositeExtract %float %75 3
+%88 = OpCompositeConstruct %v3float %86 %87 %77
+%89 = OpCompositeConstruct %mat3v3float %81 %85 %88
+%91 = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
+%92 = OpCompositeConstruct %v3float %float_4 %float_1 %float_2
+%93 = OpCompositeConstruct %v3float %float_3 %float_4 %float_1
+%94 = OpCompositeConstruct %mat3v3float %91 %92 %93
+%96 = OpCompositeExtract %v3float %89 0
+%97 = OpCompositeExtract %v3float %94 0
+%98 = OpFOrdEqual %v3bool %96 %97
+%99 = OpAll %bool %98
+%100 = OpCompositeExtract %v3float %89 1
+%101 = OpCompositeExtract %v3float %94 1
+%102 = OpFOrdEqual %v3bool %100 %101
+%103 = OpAll %bool %102
+%104 = OpLogicalAnd %bool %99 %103
+%105 = OpCompositeExtract %v3float %89 2
+%106 = OpCompositeExtract %v3float %94 2
+%107 = OpFOrdEqual %v3bool %105 %106
+%108 = OpAll %bool %107
+%109 = OpLogicalAnd %bool %104 %108
+OpBranch %70
+%70 = OpLabel
+%110 = OpPhi %bool %false %26 %109 %69
+OpStore %ok %110
+%111 = OpLoad %bool %ok
+OpSelectionMerge %113 None
+OpBranchConditional %111 %112 %113
+%112 = OpLabel
+%114 = OpLoad %v4float %f4
+%115 = OpVectorShuffle %v2float %114 %114 0 1
+%116 = OpLoad %v4float %f4
+%117 = OpVectorShuffle %v4float %116 %116 2 3 0 1
+%118 = OpLoad %v4float %f4
+%119 = OpVectorShuffle %v3float %118 %118 2 3 0
+%120 = OpLoad %v4float %f4
+%121 = OpVectorShuffle %v2float %120 %120 1 2
+%122 = OpLoad %v4float %f4
+%123 = OpVectorShuffle %v4float %122 %122 3 0 1 2
+%124 = OpLoad %v4float %f4
+%125 = OpCompositeExtract %float %124 3
+%126 = OpCompositeExtract %float %115 0
+%127 = OpCompositeExtract %float %115 1
+%128 = OpCompositeExtract %float %117 0
+%129 = OpCompositeExtract %float %117 1
+%130 = OpCompositeConstruct %v4float %126 %127 %128 %129
+%131 = OpCompositeExtract %float %117 2
+%132 = OpCompositeExtract %float %117 3
+%133 = OpCompositeExtract %float %119 0
+%134 = OpCompositeExtract %float %119 1
+%135 = OpCompositeConstruct %v4float %131 %132 %133 %134
+%136 = OpCompositeExtract %float %119 2
+%137 = OpCompositeExtract %float %121 0
+%138 = OpCompositeExtract %float %121 1
+%139 = OpCompositeExtract %float %123 0
+%140 = OpCompositeConstruct %v4float %136 %137 %138 %139
+%141 = OpCompositeExtract %float %123 1
+%142 = OpCompositeExtract %float %123 2
+%143 = OpCompositeExtract %float %123 3
+%144 = OpCompositeConstruct %v4float %141 %142 %143 %125
+%145 = OpCompositeConstruct %mat4v4float %130 %135 %140 %144
+%147 = OpCompositeConstruct %v4float %float_1 %float_2 %float_3 %float_4
+%148 = OpCompositeConstruct %v4float %float_1 %float_2 %float_3 %float_4
+%149 = OpCompositeConstruct %v4float %float_1 %float_2 %float_3 %float_4
+%150 = OpCompositeConstruct %v4float %float_1 %float_2 %float_3 %float_4
+%151 = OpCompositeConstruct %mat4v4float %147 %148 %149 %150
+%153 = OpCompositeExtract %v4float %145 0
+%154 = OpCompositeExtract %v4float %151 0
+%155 = OpFOrdEqual %v4bool %153 %154
+%156 = OpAll %bool %155
+%157 = OpCompositeExtract %v4float %145 1
+%158 = OpCompositeExtract %v4float %151 1
+%159 = OpFOrdEqual %v4bool %157 %158
+%160 = OpAll %bool %159
+%161 = OpLogicalAnd %bool %156 %160
+%162 = OpCompositeExtract %v4float %145 2
+%163 = OpCompositeExtract %v4float %151 2
+%164 = OpFOrdEqual %v4bool %162 %163
+%165 = OpAll %bool %164
+%166 = OpLogicalAnd %bool %161 %165
+%167 = OpCompositeExtract %v4float %145 3
+%168 = OpCompositeExtract %v4float %151 3
+%169 = OpFOrdEqual %v4bool %167 %168
+%170 = OpAll %bool %169
+%171 = OpLogicalAnd %bool %166 %170
+OpBranch %113
+%113 = OpLabel
+%172 = OpPhi %bool %false %70 %171 %112
+OpStore %ok %172
+%173 = OpLoad %bool %ok
+OpSelectionMerge %177 None
+OpBranchConditional %173 %175 %176
+%175 = OpLabel
+%178 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+%181 = OpLoad %v4float %178
+OpStore %174 %181
+OpBranch %177
+%176 = OpLabel
+%182 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%184 = OpLoad %v4float %182
+OpStore %174 %184
+OpBranch %177
+%177 = OpLabel
+%185 = OpLoad %v4float %174
+OpReturnValue %185
+OpFunctionEnd
diff --git a/tests/sksl/shared/MatrixConstructorsES2.glsl b/tests/sksl/shared/MatrixConstructorsES2.glsl
new file mode 100644
index 0000000..0f82d8c
--- /dev/null
+++ b/tests/sksl/shared/MatrixConstructorsES2.glsl
@@ -0,0 +1,12 @@
+
+out vec4 sk_FragColor;
+uniform vec4 colorGreen;
+uniform vec4 colorRed;
+uniform mat2 testMatrix2x2;
+vec4 main() {
+    vec4 f4 = vec4(testMatrix2x2);
+    bool ok = mat2(f4.xyz, 4.0) == mat2(1.0, 2.0, 3.0, 4.0);
+    ok = ok && mat3(f4.xy, f4.zw, f4, f4.x) == mat3(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0);
+    ok = ok && mat4(f4.xy, f4.zwxy, f4.zwx, f4.yz, f4.wxyz, f4.w) == mat4(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0);
+    return ok ? colorGreen : colorRed;
+}
diff --git a/tests/sksl/shared/MatrixConstructorsES2.metal b/tests/sksl/shared/MatrixConstructorsES2.metal
new file mode 100644
index 0000000..b053087
--- /dev/null
+++ b/tests/sksl/shared/MatrixConstructorsES2.metal
@@ -0,0 +1,69 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Uniforms {
+    float4 colorGreen;
+    float4 colorRed;
+    float2x2 testMatrix2x2;
+};
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+
+thread bool operator==(const float2x2 left, const float2x2 right);
+thread bool operator!=(const float2x2 left, const float2x2 right);
+
+thread bool operator==(const float3x3 left, const float3x3 right);
+thread bool operator!=(const float3x3 left, const float3x3 right);
+
+thread bool operator==(const float4x4 left, const float4x4 right);
+thread bool operator!=(const float4x4 left, const float4x4 right);
+
+float4 float4_from_float2x2(float2x2 x) {
+    return float4(x[0].xy, x[1].xy);
+}
+thread bool operator==(const float2x2 left, const float2x2 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]);
+}
+thread bool operator!=(const float2x2 left, const float2x2 right) {
+    return !(left == right);
+}
+float2x2 float2x2_from_float3_float(float3 x0, float x1) {
+    return float2x2(float2(x0[0], x0[1]), float2(x0[2], x1));
+}
+thread bool operator==(const float3x3 left, const float3x3 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]) &&
+           all(left[2] == right[2]);
+}
+thread bool operator!=(const float3x3 left, const float3x3 right) {
+    return !(left == right);
+}
+float3x3 float3x3_from_float2_float2_float4_float(float2 x0, float2 x1, float4 x2, float x3) {
+    return float3x3(float3(x0[0], x0[1], x1[0]), float3(x1[1], x2[0], x2[1]), float3(x2[2], x2[3], x3));
+}
+thread bool operator==(const float4x4 left, const float4x4 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]) &&
+           all(left[2] == right[2]) &&
+           all(left[3] == right[3]);
+}
+thread bool operator!=(const float4x4 left, const float4x4 right) {
+    return !(left == right);
+}
+float4x4 float4x4_from_float2_float4_float3_float2_float4_float(float2 x0, float4 x1, float3 x2, float2 x3, float4 x4, float x5) {
+    return float4x4(float4(x0[0], x0[1], x1[0], x1[1]), float4(x1[2], x1[3], x2[0], x2[1]), float4(x2[2], x3[0], x3[1], x4[0]), float4(x4[1], x4[2], x4[3], x5));
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _out;
+    (void)_out;
+    float4 f4 = float4_from_float2x2(_uniforms.testMatrix2x2);
+    bool ok = float2x2_from_float3_float(f4.xyz, 4.0) == float2x2(float2(1.0, 2.0), float2(3.0, 4.0));
+    ok = ok && float3x3_from_float2_float2_float4_float(f4.xy, f4.zw, f4, f4.x) == float3x3(float3(1.0, 2.0, 3.0), float3(4.0, 1.0, 2.0), float3(3.0, 4.0, 1.0));
+    ok = ok && float4x4_from_float2_float4_float3_float2_float4_float(f4.xy, f4.zwxy, f4.zwx, f4.yz, f4.wxyz, f4.w) == float4x4(float4(1.0, 2.0, 3.0, 4.0), float4(1.0, 2.0, 3.0, 4.0), float4(1.0, 2.0, 3.0, 4.0), float4(1.0, 2.0, 3.0, 4.0));
+    _out.sk_FragColor = ok ? _uniforms.colorGreen : _uniforms.colorRed;
+    return _out;
+}
diff --git a/tests/sksl/shared/MatrixConstructorsES3.asm.frag b/tests/sksl/shared/MatrixConstructorsES3.asm.frag
new file mode 100644
index 0000000..079b6d0
--- /dev/null
+++ b/tests/sksl/shared/MatrixConstructorsES3.asm.frag
@@ -0,0 +1,324 @@
+OpCapability Shader
+%1 = OpExtInstImport "GLSL.std.450"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_FragColor %sk_Clockwise
+OpExecutionMode %_entrypoint_v OriginUpperLeft
+OpName %sk_FragColor "sk_FragColor"
+OpName %sk_Clockwise "sk_Clockwise"
+OpName %_UniformBuffer "_UniformBuffer"
+OpMemberName %_UniformBuffer 0 "colorGreen"
+OpMemberName %_UniformBuffer 1 "colorRed"
+OpMemberName %_UniformBuffer 2 "testMatrix2x2"
+OpName %_entrypoint_v "_entrypoint_v"
+OpName %main "main"
+OpName %f4 "f4"
+OpName %ok "ok"
+OpDecorate %sk_FragColor RelaxedPrecision
+OpDecorate %sk_FragColor Location 0
+OpDecorate %sk_FragColor Index 0
+OpDecorate %sk_Clockwise BuiltIn FrontFacing
+OpMemberDecorate %_UniformBuffer 0 Offset 0
+OpMemberDecorate %_UniformBuffer 0 RelaxedPrecision
+OpMemberDecorate %_UniformBuffer 1 Offset 16
+OpMemberDecorate %_UniformBuffer 1 RelaxedPrecision
+OpMemberDecorate %_UniformBuffer 2 Offset 32
+OpMemberDecorate %_UniformBuffer 2 ColMajor
+OpMemberDecorate %_UniformBuffer 2 MatrixStride 16
+OpDecorate %_UniformBuffer Block
+OpDecorate %10 Binding 0
+OpDecorate %10 DescriptorSet 0
+OpDecorate %73 RelaxedPrecision
+OpDecorate %107 RelaxedPrecision
+OpDecorate %149 RelaxedPrecision
+OpDecorate %197 RelaxedPrecision
+OpDecorate %246 RelaxedPrecision
+OpDecorate %254 RelaxedPrecision
+OpDecorate %257 RelaxedPrecision
+OpDecorate %258 RelaxedPrecision
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%sk_FragColor = OpVariable %_ptr_Output_v4float Output
+%bool = OpTypeBool
+%_ptr_Input_bool = OpTypePointer Input %bool
+%sk_Clockwise = OpVariable %_ptr_Input_bool Input
+%v2float = OpTypeVector %float 2
+%mat2v2float = OpTypeMatrix %v2float 2
+%_UniformBuffer = OpTypeStruct %v4float %v4float %mat2v2float
+%_ptr_Uniform__UniformBuffer = OpTypePointer Uniform %_UniformBuffer
+%10 = OpVariable %_ptr_Uniform__UniformBuffer Uniform
+%void = OpTypeVoid
+%17 = OpTypeFunction %void
+%float_0 = OpConstant %float 0
+%20 = OpConstantComposite %v2float %float_0 %float_0
+%_ptr_Function_v2float = OpTypePointer Function %v2float
+%24 = OpTypeFunction %v4float %_ptr_Function_v2float
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%_ptr_Uniform_mat2v2float = OpTypePointer Uniform %mat2v2float
+%int = OpTypeInt 32 1
+%int_2 = OpConstant %int 2
+%_ptr_Function_bool = OpTypePointer Function %bool
+%v3float = OpTypeVector %float 3
+%mat2v3float = OpTypeMatrix %v3float 2
+%float_1 = OpConstant %float 1
+%float_2 = OpConstant %float 2
+%float_3 = OpConstant %float 3
+%float_4 = OpConstant %float 4
+%v3bool = OpTypeVector %bool 3
+%false = OpConstantFalse %bool
+%mat2v4float = OpTypeMatrix %v4float 2
+%v4bool = OpTypeVector %bool 4
+%mat3v3float = OpTypeMatrix %v3float 3
+%mat4v2float = OpTypeMatrix %v2float 4
+%v2bool = OpTypeVector %bool 2
+%mat4v3float = OpTypeMatrix %v3float 4
+%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
+%int_0 = OpConstant %int 0
+%int_1 = OpConstant %int 1
+%_entrypoint_v = OpFunction %void None %17
+%18 = OpLabel
+%21 = OpVariable %_ptr_Function_v2float Function
+OpStore %21 %20
+%23 = OpFunctionCall %v4float %main %21
+OpStore %sk_FragColor %23
+OpReturn
+OpFunctionEnd
+%main = OpFunction %v4float None %24
+%25 = OpFunctionParameter %_ptr_Function_v2float
+%26 = OpLabel
+%f4 = OpVariable %_ptr_Function_v4float Function
+%ok = OpVariable %_ptr_Function_bool Function
+%247 = OpVariable %_ptr_Function_v4float Function
+%29 = OpAccessChain %_ptr_Uniform_mat2v2float %10 %int_2
+%33 = OpLoad %mat2v2float %29
+%34 = OpCompositeExtract %float %33 0 0
+%35 = OpCompositeExtract %float %33 0 1
+%36 = OpCompositeExtract %float %33 1 0
+%37 = OpCompositeExtract %float %33 1 1
+%38 = OpCompositeConstruct %v4float %34 %35 %36 %37
+OpStore %f4 %38
+%41 = OpLoad %v4float %f4
+%42 = OpLoad %v4float %f4
+%43 = OpVectorShuffle %v2float %42 %42 0 1
+%44 = OpCompositeExtract %float %41 0
+%45 = OpCompositeExtract %float %41 1
+%46 = OpCompositeExtract %float %41 2
+%47 = OpCompositeConstruct %v3float %44 %45 %46
+%49 = OpCompositeExtract %float %41 3
+%50 = OpCompositeExtract %float %43 0
+%51 = OpCompositeExtract %float %43 1
+%52 = OpCompositeConstruct %v3float %49 %50 %51
+%53 = OpCompositeConstruct %mat2v3float %47 %52
+%59 = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
+%60 = OpCompositeConstruct %v3float %float_4 %float_1 %float_2
+%61 = OpCompositeConstruct %mat2v3float %59 %60
+%63 = OpCompositeExtract %v3float %53 0
+%64 = OpCompositeExtract %v3float %61 0
+%65 = OpFOrdEqual %v3bool %63 %64
+%66 = OpAll %bool %65
+%67 = OpCompositeExtract %v3float %53 1
+%68 = OpCompositeExtract %v3float %61 1
+%69 = OpFOrdEqual %v3bool %67 %68
+%70 = OpAll %bool %69
+%71 = OpLogicalAnd %bool %66 %70
+OpStore %ok %71
+%73 = OpLoad %bool %ok
+OpSelectionMerge %75 None
+OpBranchConditional %73 %74 %75
+%74 = OpLabel
+%76 = OpLoad %v4float %f4
+%77 = OpVectorShuffle %v3float %76 %76 0 1 2
+%78 = OpLoad %v4float %f4
+%79 = OpVectorShuffle %v4float %78 %78 3 0 1 2
+%80 = OpLoad %v4float %f4
+%81 = OpCompositeExtract %float %80 3
+%82 = OpCompositeExtract %float %77 0
+%83 = OpCompositeExtract %float %77 1
+%84 = OpCompositeExtract %float %77 2
+%85 = OpCompositeExtract %float %79 0
+%86 = OpCompositeConstruct %v4float %82 %83 %84 %85
+%87 = OpCompositeExtract %float %79 1
+%88 = OpCompositeExtract %float %79 2
+%89 = OpCompositeExtract %float %79 3
+%90 = OpCompositeConstruct %v4float %87 %88 %89 %81
+%91 = OpCompositeConstruct %mat2v4float %86 %90
+%93 = OpCompositeConstruct %v4float %float_1 %float_2 %float_3 %float_4
+%94 = OpCompositeConstruct %v4float %float_1 %float_2 %float_3 %float_4
+%95 = OpCompositeConstruct %mat2v4float %93 %94
+%97 = OpCompositeExtract %v4float %91 0
+%98 = OpCompositeExtract %v4float %95 0
+%99 = OpFOrdEqual %v4bool %97 %98
+%100 = OpAll %bool %99
+%101 = OpCompositeExtract %v4float %91 1
+%102 = OpCompositeExtract %v4float %95 1
+%103 = OpFOrdEqual %v4bool %101 %102
+%104 = OpAll %bool %103
+%105 = OpLogicalAnd %bool %100 %104
+OpBranch %75
+%75 = OpLabel
+%106 = OpPhi %bool %false %26 %105 %74
+OpStore %ok %106
+%107 = OpLoad %bool %ok
+OpSelectionMerge %109 None
+OpBranchConditional %107 %108 %109
+%108 = OpLabel
+%110 = OpLoad %v4float %f4
+%111 = OpVectorShuffle %v2float %110 %110 0 1
+%112 = OpLoad %v4float %f4
+%113 = OpVectorShuffle %v2float %112 %112 2 3
+%114 = OpLoad %v4float %f4
+%115 = OpLoad %v4float %f4
+%116 = OpCompositeExtract %float %115 0
+%117 = OpCompositeExtract %float %111 0
+%118 = OpCompositeExtract %float %111 1
+%119 = OpCompositeExtract %float %113 0
+%120 = OpCompositeConstruct %v3float %117 %118 %119
+%121 = OpCompositeExtract %float %113 1
+%122 = OpCompositeExtract %float %114 0
+%123 = OpCompositeExtract %float %114 1
+%124 = OpCompositeConstruct %v3float %121 %122 %123
+%125 = OpCompositeExtract %float %114 2
+%126 = OpCompositeExtract %float %114 3
+%127 = OpCompositeConstruct %v3float %125 %126 %116
+%128 = OpCompositeConstruct %mat3v3float %120 %124 %127
+%130 = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
+%131 = OpCompositeConstruct %v3float %float_4 %float_1 %float_2
+%132 = OpCompositeConstruct %v3float %float_3 %float_4 %float_1
+%133 = OpCompositeConstruct %mat3v3float %130 %131 %132
+%134 = OpCompositeExtract %v3float %128 0
+%135 = OpCompositeExtract %v3float %133 0
+%136 = OpFOrdEqual %v3bool %134 %135
+%137 = OpAll %bool %136
+%138 = OpCompositeExtract %v3float %128 1
+%139 = OpCompositeExtract %v3float %133 1
+%140 = OpFOrdEqual %v3bool %138 %139
+%141 = OpAll %bool %140
+%142 = OpLogicalAnd %bool %137 %141
+%143 = OpCompositeExtract %v3float %128 2
+%144 = OpCompositeExtract %v3float %133 2
+%145 = OpFOrdEqual %v3bool %143 %144
+%146 = OpAll %bool %145
+%147 = OpLogicalAnd %bool %142 %146
+OpBranch %109
+%109 = OpLabel
+%148 = OpPhi %bool %false %75 %147 %108
+OpStore %ok %148
+%149 = OpLoad %bool %ok
+OpSelectionMerge %151 None
+OpBranchConditional %149 %150 %151
+%150 = OpLabel
+%152 = OpLoad %v4float %f4
+%153 = OpVectorShuffle %v3float %152 %152 0 1 2
+%154 = OpLoad %v4float %f4
+%155 = OpVectorShuffle %v4float %154 %154 3 0 1 2
+%156 = OpLoad %v4float %f4
+%157 = OpCompositeExtract %float %156 3
+%158 = OpCompositeExtract %float %153 0
+%159 = OpCompositeExtract %float %153 1
+%160 = OpCompositeConstruct %v2float %158 %159
+%161 = OpCompositeExtract %float %153 2
+%162 = OpCompositeExtract %float %155 0
+%163 = OpCompositeConstruct %v2float %161 %162
+%164 = OpCompositeExtract %float %155 1
+%165 = OpCompositeExtract %float %155 2
+%166 = OpCompositeConstruct %v2float %164 %165
+%167 = OpCompositeExtract %float %155 3
+%168 = OpCompositeConstruct %v2float %167 %157
+%169 = OpCompositeConstruct %mat4v2float %160 %163 %166 %168
+%171 = OpCompositeConstruct %v2float %float_1 %float_2
+%172 = OpCompositeConstruct %v2float %float_3 %float_4
+%173 = OpCompositeConstruct %v2float %float_1 %float_2
+%174 = OpCompositeConstruct %v2float %float_3 %float_4
+%175 = OpCompositeConstruct %mat4v2float %171 %172 %173 %174
+%177 = OpCompositeExtract %v2float %169 0
+%178 = OpCompositeExtract %v2float %175 0
+%179 = OpFOrdEqual %v2bool %177 %178
+%180 = OpAll %bool %179
+%181 = OpCompositeExtract %v2float %169 1
+%182 = OpCompositeExtract %v2float %175 1
+%183 = OpFOrdEqual %v2bool %181 %182
+%184 = OpAll %bool %183
+%185 = OpLogicalAnd %bool %180 %184
+%186 = OpCompositeExtract %v2float %169 2
+%187 = OpCompositeExtract %v2float %175 2
+%188 = OpFOrdEqual %v2bool %186 %187
+%189 = OpAll %bool %188
+%190 = OpLogicalAnd %bool %185 %189
+%191 = OpCompositeExtract %v2float %169 3
+%192 = OpCompositeExtract %v2float %175 3
+%193 = OpFOrdEqual %v2bool %191 %192
+%194 = OpAll %bool %193
+%195 = OpLogicalAnd %bool %190 %194
+OpBranch %151
+%151 = OpLabel
+%196 = OpPhi %bool %false %109 %195 %150
+OpStore %ok %196
+%197 = OpLoad %bool %ok
+OpSelectionMerge %199 None
+OpBranchConditional %197 %198 %199
+%198 = OpLabel
+%200 = OpLoad %v4float %f4
+%201 = OpCompositeExtract %float %200 0
+%202 = OpLoad %v4float %f4
+%203 = OpVectorShuffle %v4float %202 %202 1 2 3 0
+%204 = OpLoad %v4float %f4
+%205 = OpVectorShuffle %v4float %204 %204 1 2 3 0
+%206 = OpLoad %v4float %f4
+%207 = OpVectorShuffle %v3float %206 %206 1 2 3
+%208 = OpCompositeExtract %float %203 0
+%209 = OpCompositeExtract %float %203 1
+%210 = OpCompositeConstruct %v3float %201 %208 %209
+%211 = OpCompositeExtract %float %203 2
+%212 = OpCompositeExtract %float %203 3
+%213 = OpCompositeExtract %float %205 0
+%214 = OpCompositeConstruct %v3float %211 %212 %213
+%215 = OpCompositeExtract %float %205 1
+%216 = OpCompositeExtract %float %205 2
+%217 = OpCompositeExtract %float %205 3
+%218 = OpCompositeConstruct %v3float %215 %216 %217
+%219 = OpCompositeConstruct %mat4v3float %210 %214 %218 %207
+%221 = OpCompositeConstruct %v3float %float_1 %float_2 %float_3
+%222 = OpCompositeConstruct %v3float %float_4 %float_1 %float_2
+%223 = OpCompositeConstruct %v3float %float_3 %float_4 %float_1
+%224 = OpCompositeConstruct %v3float %float_2 %float_3 %float_4
+%225 = OpCompositeConstruct %mat4v3float %221 %222 %223 %224
+%226 = OpCompositeExtract %v3float %219 0
+%227 = OpCompositeExtract %v3float %225 0
+%228 = OpFOrdEqual %v3bool %226 %227
+%229 = OpAll %bool %228
+%230 = OpCompositeExtract %v3float %219 1
+%231 = OpCompositeExtract %v3float %225 1
+%232 = OpFOrdEqual %v3bool %230 %231
+%233 = OpAll %bool %232
+%234 = OpLogicalAnd %bool %229 %233
+%235 = OpCompositeExtract %v3float %219 2
+%236 = OpCompositeExtract %v3float %225 2
+%237 = OpFOrdEqual %v3bool %235 %236
+%238 = OpAll %bool %237
+%239 = OpLogicalAnd %bool %234 %238
+%240 = OpCompositeExtract %v3float %219 3
+%241 = OpCompositeExtract %v3float %225 3
+%242 = OpFOrdEqual %v3bool %240 %241
+%243 = OpAll %bool %242
+%244 = OpLogicalAnd %bool %239 %243
+OpBranch %199
+%199 = OpLabel
+%245 = OpPhi %bool %false %151 %244 %198
+OpStore %ok %245
+%246 = OpLoad %bool %ok
+OpSelectionMerge %250 None
+OpBranchConditional %246 %248 %249
+%248 = OpLabel
+%251 = OpAccessChain %_ptr_Uniform_v4float %10 %int_0
+%254 = OpLoad %v4float %251
+OpStore %247 %254
+OpBranch %250
+%249 = OpLabel
+%255 = OpAccessChain %_ptr_Uniform_v4float %10 %int_1
+%257 = OpLoad %v4float %255
+OpStore %247 %257
+OpBranch %250
+%250 = OpLabel
+%258 = OpLoad %v4float %247
+OpReturnValue %258
+OpFunctionEnd
diff --git a/tests/sksl/shared/MatrixConstructorsES3.glsl b/tests/sksl/shared/MatrixConstructorsES3.glsl
new file mode 100644
index 0000000..c7aa4d5
--- /dev/null
+++ b/tests/sksl/shared/MatrixConstructorsES3.glsl
@@ -0,0 +1,14 @@
+
+out vec4 sk_FragColor;
+uniform vec4 colorGreen;
+uniform vec4 colorRed;
+uniform mat2 testMatrix2x2;
+vec4 main() {
+    vec4 f4 = vec4(testMatrix2x2);
+    bool ok = mat2x3(f4, f4.xy) == mat2x3(1.0, 2.0, 3.0, 4.0, 1.0, 2.0);
+    ok = ok && mat2x4(f4.xyz, f4.wxyz, f4.w) == mat2x4(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0);
+    ok = ok && mat3(f4.xy, f4.zw, f4, f4.x) == mat3(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0);
+    ok = ok && mat4x2(f4.xyz, f4.wxyz, f4.w) == mat4x2(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0);
+    ok = ok && mat4x3(f4.x, f4.yzwx, f4.yzwx, f4.yzw) == mat4x3(1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0);
+    return ok ? colorGreen : colorRed;
+}
diff --git a/tests/sksl/shared/MatrixConstructorsES3.metal b/tests/sksl/shared/MatrixConstructorsES3.metal
new file mode 100644
index 0000000..c9eda63
--- /dev/null
+++ b/tests/sksl/shared/MatrixConstructorsES3.metal
@@ -0,0 +1,99 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+using namespace metal;
+struct Uniforms {
+    float4 colorGreen;
+    float4 colorRed;
+    float2x2 testMatrix2x2;
+};
+struct Inputs {
+};
+struct Outputs {
+    float4 sk_FragColor [[color(0)]];
+};
+
+thread bool operator==(const float2x3 left, const float2x3 right);
+thread bool operator!=(const float2x3 left, const float2x3 right);
+
+thread bool operator==(const float2x4 left, const float2x4 right);
+thread bool operator!=(const float2x4 left, const float2x4 right);
+
+thread bool operator==(const float3x3 left, const float3x3 right);
+thread bool operator!=(const float3x3 left, const float3x3 right);
+
+thread bool operator==(const float4x2 left, const float4x2 right);
+thread bool operator!=(const float4x2 left, const float4x2 right);
+
+thread bool operator==(const float4x3 left, const float4x3 right);
+thread bool operator!=(const float4x3 left, const float4x3 right);
+
+float4 float4_from_float2x2(float2x2 x) {
+    return float4(x[0].xy, x[1].xy);
+}
+thread bool operator==(const float2x3 left, const float2x3 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]);
+}
+thread bool operator!=(const float2x3 left, const float2x3 right) {
+    return !(left == right);
+}
+float2x3 float2x3_from_float4_float2(float4 x0, float2 x1) {
+    return float2x3(float3(x0[0], x0[1]), float3(x0[2], x0[3]), float3(x1[0], x1[1]));
+}
+thread bool operator==(const float2x4 left, const float2x4 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]);
+}
+thread bool operator!=(const float2x4 left, const float2x4 right) {
+    return !(left == right);
+}
+float2x4 float2x4_from_float3_float4_float(float3 x0, float4 x1, float x2) {
+    return float2x4(float4(x0[0], x0[1]), float4(x0[2], x1[0]), float4(x1[1], x1[2]), float4(x1[3], x2));
+}
+thread bool operator==(const float3x3 left, const float3x3 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]) &&
+           all(left[2] == right[2]);
+}
+thread bool operator!=(const float3x3 left, const float3x3 right) {
+    return !(left == right);
+}
+float3x3 float3x3_from_float2_float2_float4_float(float2 x0, float2 x1, float4 x2, float x3) {
+    return float3x3(float3(x0[0], x0[1], x1[0]), float3(x1[1], x2[0], x2[1]), float3(x2[2], x2[3], x3));
+}
+thread bool operator==(const float4x2 left, const float4x2 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]) &&
+           all(left[2] == right[2]) &&
+           all(left[3] == right[3]);
+}
+thread bool operator!=(const float4x2 left, const float4x2 right) {
+    return !(left == right);
+}
+float4x2 float4x2_from_float3_float4_float(float3 x0, float4 x1, float x2) {
+    return float4x2(float2(x0[0], x0[1], x0[2], x1[0]), float2(x1[1], x1[2], x1[3], x2));
+}
+thread bool operator==(const float4x3 left, const float4x3 right) {
+    return all(left[0] == right[0]) &&
+           all(left[1] == right[1]) &&
+           all(left[2] == right[2]) &&
+           all(left[3] == right[3]);
+}
+thread bool operator!=(const float4x3 left, const float4x3 right) {
+    return !(left == right);
+}
+float4x3 float4x3_from_float_float4_float4_float3(float x0, float4 x1, float4 x2, float3 x3) {
+    return float4x3(float3(x0, x1[0], x1[1], x1[2]), float3(x1[3], x2[0], x2[1], x2[2]), float3(x2[3], x3[0], x3[1], x3[2]));
+}
+fragment Outputs fragmentMain(Inputs _in [[stage_in]], constant Uniforms& _uniforms [[buffer(0)]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
+    Outputs _out;
+    (void)_out;
+    float4 f4 = float4_from_float2x2(_uniforms.testMatrix2x2);
+    bool ok = float2x3_from_float4_float2(f4, f4.xy) == float2x3(float3(1.0, 2.0, 3.0), float3(4.0, 1.0, 2.0));
+    ok = ok && float2x4_from_float3_float4_float(f4.xyz, f4.wxyz, f4.w) == float2x4(float4(1.0, 2.0, 3.0, 4.0), float4(1.0, 2.0, 3.0, 4.0));
+    ok = ok && float3x3_from_float2_float2_float4_float(f4.xy, f4.zw, f4, f4.x) == float3x3(float3(1.0, 2.0, 3.0), float3(4.0, 1.0, 2.0), float3(3.0, 4.0, 1.0));
+    ok = ok && float4x2_from_float3_float4_float(f4.xyz, f4.wxyz, f4.w) == float4x2(float2(1.0, 2.0), float2(3.0, 4.0), float2(1.0, 2.0), float2(3.0, 4.0));
+    ok = ok && float4x3_from_float_float4_float4_float3(f4.x, f4.yzwx, f4.yzwx, f4.yzw) == float4x3(float3(1.0, 2.0, 3.0), float3(4.0, 1.0, 2.0), float3(3.0, 4.0, 1.0), float3(2.0, 3.0, 4.0));
+    _out.sk_FragColor = ok ? _uniforms.colorGreen : _uniforms.colorRed;
+    return _out;
+}