Optimize indexing into an array with a constant-expression.

Previously, SkSL was unable to resolve the constant expression `x[y]`
for a constant-array `x` and a constant-integer-scalar `y`. Now, if `x`
and `y` are known, we can replace `x[y]` with the indexed array element.

Note that we need to be careful here, as it's not a valid optimization
to eliminate array elements that have side effects. We preserve side-
effecting expressions using the comma operator.

Change-Id: I5721337eb42b48c0b05f919c1cadfae19dd3b84f
Bug: skia:12472
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/469839
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2021-11-10 13:53:25 -05:00 committed by SkCQ
parent 5e62cd0a00
commit 76c1ff1566
6 changed files with 57 additions and 69 deletions

View File

@ -17,15 +17,15 @@ bool test() {
const int b [x[x[z]]] = int[2](1, 2);
const int c [x[x[x[z]]]] = int[3](1, 2, 3);
// The unreferenced array elements are safe to eliminate.
int flatten0 = (int[3](side_effecting(1), 2, 3))[0];
int flatten1 = (int[3](1, side_effecting(2), 3))[1];
int flatten2 = (int[3](1, 2, side_effecting(3)))[2];
// Constant-expression arrays can be optimized.
int flatten0 = (int[3](1, 2, 3))[0];
int flatten1 = (int[3](1, 2, 3))[1];
int flatten2 = (int[3](1, 2, 3))[2];
// Some unreferenced array elements have a side effect and are not safe to eliminate.
// Non-constant-expression arrays are not eligible for optimization.
int noFlatten0 = (int[3](1, side_effecting(2), 3))[0];
int noFlatten1 = (int[3](side_effecting(1), 2, 3))[1];
int noFlatten2 = (int[3](side_effecting(1), side_effecting(2), 3))[2];
int noFlatten2 = (int[3](1, 2, side_effecting(3)))[2];
return (x == xx) && !(x != xx) && (x != y) && !(x == y) &&
(x[0] == y[0]) && (c == x) &&

View File

@ -7,6 +7,8 @@
#include "src/sksl/SkSLConstantFolder.h"
#include "src/sksl/SkSLProgramSettings.h"
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLConstructorArray.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLLiteral.h"
#include "src/sksl/ir/SkSLSwizzle.h"
@ -100,11 +102,22 @@ std::unique_ptr<Expression> IndexExpression::Make(const Context& context,
// Swizzling is harmless and can unlock further simplifications for some base types.
return Swizzle::Make(context, std::move(base), ComponentArray{(int8_t)indexValue});
}
if (baseType.isArray()) {
// Indexing an constant array constructor with a constant index can just pluck out
// the requested value from the array.
const Expression* baseExpr = ConstantFolder::GetConstantValueForVariable(*base);
if (baseExpr->is<ConstructorArray>() && baseExpr->isCompileTimeConstant()) {
const ConstructorArray& arrayCtor = baseExpr->as<ConstructorArray>();
const ExpressionArray& arguments = arrayCtor.arguments();
SkASSERT(arguments.count() == baseType.columns());
return arguments[indexValue]->clone();
}
}
}
}
// TODO(skia:12472): constantArray[constantExpr] should be compile-time evaluated.
return std::make_unique<IndexExpression>(context, std::move(base), std::move(index));
}

View File

@ -1,9 +1,18 @@
### Compilation failed:
error: 16: array size must be an integer
error: 17: array size must be an integer
error: 17: expected 'int[1]', but found 'int[2]'
error: 18: array size must be an integer
error: 18: expected 'int[1]', but found 'int[3]'
error: 31: unknown identifier 'c'
6 errors
out vec4 sk_FragColor;
uniform vec4 colorRed;
uniform vec4 colorGreen;
int globalValue = 0;
int side_effecting_ii(int value) {
globalValue++;
return value;
}
vec4 main() {
int _7_flatten0 = 1;
int _8_flatten1 = 2;
int _9_flatten2 = 3;
int _10_noFlatten0 = int[3](1, side_effecting_ii(2), 3)[0];
int _11_noFlatten1 = int[3](side_effecting_ii(1), 2, 3)[1];
int _12_noFlatten2 = int[3](1, 2, side_effecting_ii(3))[2];
return (_7_flatten0 == _10_noFlatten0 && _8_flatten1 == _11_noFlatten1) && _9_flatten2 == _12_noFlatten2 ? colorGreen : colorRed;
}

View File

@ -5,21 +5,13 @@ OpEntryPoint Fragment %_entrypoint_v "_entrypoint" %sk_FragColor %sk_Clockwise
OpExecutionMode %_entrypoint_v OriginUpperLeft
OpName %sk_FragColor "sk_FragColor"
OpName %sk_Clockwise "sk_Clockwise"
OpName %test "test"
OpName %_entrypoint_v "_entrypoint_v"
OpName %main "main"
OpDecorate %sk_FragColor RelaxedPrecision
OpDecorate %sk_FragColor Location 0
OpDecorate %sk_FragColor Index 0
OpDecorate %sk_Clockwise BuiltIn FrontFacing
OpDecorate %test RelaxedPrecision
OpDecorate %_arr_float_int_4 ArrayStride 16
OpDecorate %17 RelaxedPrecision
OpDecorate %33 RelaxedPrecision
OpDecorate %36 RelaxedPrecision
OpDecorate %39 RelaxedPrecision
OpDecorate %42 RelaxedPrecision
OpDecorate %43 RelaxedPrecision
OpDecorate %24 RelaxedPrecision
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
@ -27,45 +19,25 @@ OpDecorate %43 RelaxedPrecision
%bool = OpTypeBool
%_ptr_Input_bool = OpTypePointer Input %bool
%sk_Clockwise = OpVariable %_ptr_Input_bool Input
%int = OpTypeInt 32 1
%int_4 = OpConstant %int 4
%_arr_float_int_4 = OpTypeArray %float %int_4
%_ptr_Private__arr_float_int_4 = OpTypePointer Private %_arr_float_int_4
%test = OpVariable %_ptr_Private__arr_float_int_4 Private
%float_0 = OpConstant %float 0
%float_1 = OpConstant %float 1
%void = OpTypeVoid
%20 = OpTypeFunction %void
%12 = OpTypeFunction %void
%v2float = OpTypeVector %float 2
%23 = OpConstantComposite %v2float %float_0 %float_0
%float_0 = OpConstant %float 0
%16 = OpConstantComposite %v2float %float_0 %float_0
%_ptr_Function_v2float = OpTypePointer Function %v2float
%27 = OpTypeFunction %v4float %_ptr_Function_v2float
%int_0 = OpConstant %int 0
%_ptr_Private_float = OpTypePointer Private %float
%int_1 = OpConstant %int 1
%int_2 = OpConstant %int 2
%int_3 = OpConstant %int 3
%_entrypoint_v = OpFunction %void None %20
%21 = OpLabel
%24 = OpVariable %_ptr_Function_v2float Function
OpStore %24 %23
%26 = OpFunctionCall %v4float %main %24
OpStore %sk_FragColor %26
%20 = OpTypeFunction %v4float %_ptr_Function_v2float
%float_1 = OpConstant %float 1
%24 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1
%_entrypoint_v = OpFunction %void None %12
%13 = OpLabel
%17 = OpVariable %_ptr_Function_v2float Function
OpStore %17 %16
%19 = OpFunctionCall %v4float %main %17
OpStore %sk_FragColor %19
OpReturn
OpFunctionEnd
%main = OpFunction %v4float None %27
%28 = OpFunctionParameter %_ptr_Function_v2float
%29 = OpLabel
%17 = OpCompositeConstruct %_arr_float_int_4 %float_0 %float_1 %float_0 %float_1
OpStore %test %17
%31 = OpAccessChain %_ptr_Private_float %test %int_0
%33 = OpLoad %float %31
%35 = OpAccessChain %_ptr_Private_float %test %int_1
%36 = OpLoad %float %35
%38 = OpAccessChain %_ptr_Private_float %test %int_2
%39 = OpLoad %float %38
%41 = OpAccessChain %_ptr_Private_float %test %int_3
%42 = OpLoad %float %41
%43 = OpCompositeConstruct %v4float %33 %36 %39 %42
OpReturnValue %43
%main = OpFunction %v4float None %20
%21 = OpFunctionParameter %_ptr_Function_v2float
%22 = OpLabel
OpReturnValue %24
OpFunctionEnd

View File

@ -1,6 +1,5 @@
out vec4 sk_FragColor;
const float test[4] = float[4](0.0, 1.0, 0.0, 1.0);
vec4 main() {
return vec4(test[0], test[1], test[2], test[3]);
return vec4(0.0, 1.0, 0.0, 1.0);
}

View File

@ -6,14 +6,9 @@ struct Inputs {
struct Outputs {
half4 sk_FragColor [[color(0)]];
};
struct Globals {
const array<half, 4> test;
};
fragment Outputs fragmentMain(Inputs _in [[stage_in]], bool _frontFacing [[front_facing]], float4 _fragCoord [[position]]) {
Globals _globals{array<half, 4>{0.0h, 1.0h, 0.0h, 1.0h}};
(void)_globals;
Outputs _out;
(void)_out;
_out.sk_FragColor = half4(_globals.test[0], _globals.test[1], _globals.test[2], _globals.test[3]);
_out.sk_FragColor = half4(0.0h, 1.0h, 0.0h, 1.0h);
return _out;
}