Flutter Engine
The Flutter Engine
SkSLIndexExpression.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
23#include "src/sksl/ir/SkSLSymbolTable.h" // IWYU pragma: keep
26
27#include <cstdint>
28#include <optional>
29
30namespace SkSL {
31
32static bool index_out_of_range(const Context& context, Position pos, SKSL_INT index,
33 const Expression& base) {
34 if (index >= 0) {
35 if (base.type().columns() == Type::kUnsizedArray) {
36 return false;
37 } else if (index < base.type().columns()) {
38 return false;
39 }
40 }
41 context.fErrors->error(pos, "index " + std::to_string(index) + " out of range for '" +
42 base.type().displayName() + "'");
43 return true;
44}
45
46const Type& IndexExpression::IndexType(const Context& context, const Type& type) {
47 if (type.isMatrix()) {
48 if (type.componentType().matches(*context.fTypes.fFloat)) {
49 switch (type.rows()) {
50 case 2: return *context.fTypes.fFloat2;
51 case 3: return *context.fTypes.fFloat3;
52 case 4: return *context.fTypes.fFloat4;
53 default: SkASSERT(false);
54 }
55 } else if (type.componentType().matches(*context.fTypes.fHalf)) {
56 switch (type.rows()) {
57 case 2: return *context.fTypes.fHalf2;
58 case 3: return *context.fTypes.fHalf3;
59 case 4: return *context.fTypes.fHalf4;
60 default: SkASSERT(false);
61 }
62 }
63 }
64 return type.componentType();
65}
66
67std::unique_ptr<Expression> IndexExpression::Convert(const Context& context,
69 std::unique_ptr<Expression> base,
70 std::unique_ptr<Expression> index) {
71 // Convert an array type reference: `int[10]`.
72 if (base->is<TypeReference>()) {
73 const Type& baseType = base->as<TypeReference>().value();
74 SKSL_INT arraySize = baseType.convertArraySize(context, pos, std::move(index));
75 if (!arraySize) {
76 return nullptr;
77 }
79 context,
80 pos,
81 context.fSymbolTable->addArrayDimension(context, &baseType, arraySize));
82 }
83 // Convert an index expression with an expression inside of it: `arr[a * 3]`.
84 const Type& baseType = base->type();
85 if (!baseType.isArray() && !baseType.isMatrix() && !baseType.isVector()) {
86 context.fErrors->error(base->fPosition,
87 "expected array, but found '" + baseType.displayName() + "'");
88 return nullptr;
89 }
90 if (!index->type().isInteger()) {
91 index = context.fTypes.fInt->coerceExpression(std::move(index), context);
92 if (!index) {
93 return nullptr;
94 }
95 }
96 // Perform compile-time bounds checking on constant-expression indices.
98 if (indexExpr->isIntLiteral()) {
99 SKSL_INT indexValue = indexExpr->as<Literal>().intValue();
100 if (index_out_of_range(context, index->fPosition, indexValue, *base)) {
101 return nullptr;
102 }
103 }
104 return IndexExpression::Make(context, pos, std::move(base), std::move(index));
105}
106
107std::unique_ptr<Expression> IndexExpression::Make(const Context& context,
109 std::unique_ptr<Expression> base,
110 std::unique_ptr<Expression> index) {
111 const Type& baseType = base->type();
112 SkASSERT(baseType.isArray() || baseType.isMatrix() || baseType.isVector());
113 SkASSERT(index->type().isInteger());
114
116 if (indexExpr->isIntLiteral()) {
117 SKSL_INT indexValue = indexExpr->as<Literal>().intValue();
118 if (!index_out_of_range(context, index->fPosition, indexValue, *base)) {
119 if (baseType.isVector()) {
120 // Constant array indexes on vectors can be converted to swizzles: `v[2]` --> `v.z`.
121 // Swizzling is harmless and can unlock further simplifications for some base types.
122 return Swizzle::Make(context, pos, std::move(base),
123 ComponentArray{(int8_t)indexValue});
124 }
125
126 if (baseType.isArray() && !Analysis::HasSideEffects(*base)) {
127 // Indexing an constant array constructor with a constant index can just pluck out
128 // the requested value from the array.
130 if (baseExpr->is<ConstructorArray>()) {
131 const ConstructorArray& arrayCtor = baseExpr->as<ConstructorArray>();
132 const ExpressionArray& arguments = arrayCtor.arguments();
133 SkASSERT(arguments.size() == baseType.columns());
134
135 return arguments[indexValue]->clone(pos);
136 }
137 }
138
139 if (baseType.isMatrix() && !Analysis::HasSideEffects(*base)) {
140 // Matrices can be constructed with vectors that don't line up on column boundaries,
141 // so extracting out the values from the constructor can be tricky. Fortunately, we
142 // can reconstruct an equivalent vector using `getConstantValue`. If we
143 // can't extract the data using `getConstantValue`, it wasn't constant and
144 // we're not obligated to simplify anything.
146 int vecWidth = baseType.rows();
147 const Type& vecType = baseType.columnType(context);
148 indexValue *= vecWidth;
149
150 double ctorArgs[4];
151 bool allConstant = true;
152 for (int slot = 0; slot < vecWidth; ++slot) {
153 std::optional<double> slotVal = baseExpr->getConstantValue(indexValue + slot);
154 if (slotVal.has_value()) {
155 ctorArgs[slot] = *slotVal;
156 } else {
157 allConstant = false;
158 break;
159 }
160 }
161
162 if (allConstant) {
163 return ConstructorCompound::MakeFromConstants(context, pos, vecType, ctorArgs);
164 }
165 }
166 }
167 }
168
169 return std::make_unique<IndexExpression>(context, pos, std::move(base), std::move(index));
170}
171
173 return this->base()->description(OperatorPrecedence::kPostfix) + "[" +
174 this->index()->description(OperatorPrecedence::kExpression) + "]";
175}
176
177} // namespace SkSL
SkPoint pos
#define SkASSERT(cond)
Definition: SkAssert.h:116
int64_t SKSL_INT
Definition: SkSLDefines.h:16
GLenum type
const std::unique_ptr< Type > fFloat2
const std::unique_ptr< Type > fHalf4
const std::unique_ptr< Type > fInt
const std::unique_ptr< Type > fFloat4
const std::unique_ptr< Type > fHalf2
const std::unique_ptr< Type > fHalf3
const std::unique_ptr< Type > fFloat
const std::unique_ptr< Type > fFloat3
const std::unique_ptr< Type > fHalf
static const Expression * GetConstantValueForVariable(const Expression &value)
static std::unique_ptr< Expression > MakeFromConstants(const Context &context, Position pos, const Type &type, const double values[])
const BuiltinTypes & fTypes
Definition: SkSLContext.h:30
ErrorReporter * fErrors
Definition: SkSLContext.h:36
SymbolTable * fSymbolTable
Definition: SkSLContext.h:48
void error(Position position, std::string_view msg)
bool isIntLiteral() const
const Type & type() const
virtual std::optional< double > getConstantValue(int n) const
std::string description() const final
bool is() const
Definition: SkSLIRNode.h:124
const T & as() const
Definition: SkSLIRNode.h:133
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > base, std::unique_ptr< Expression > index)
std::unique_ptr< Expression > & base()
static std::unique_ptr< Expression > Convert(const Context &context, Position pos, std::unique_ptr< Expression > base, std::unique_ptr< Expression > index)
static const Type & IndexType(const Context &context, const Type &type)
std::unique_ptr< Expression > & index()
ExpressionArray & arguments()
static std::unique_ptr< Expression > Make(const Context &context, Position pos, std::unique_ptr< Expression > expr, ComponentArray inComponents)
const Type * addArrayDimension(const Context &context, const Type *type, int arraySize)
static std::unique_ptr< TypeReference > Convert(const Context &context, Position pos, const Type *type)
virtual bool isArray() const
Definition: SkSLType.h:532
virtual bool isVector() const
Definition: SkSLType.h:524
virtual int rows() const
Definition: SkSLType.h:438
virtual const Type & componentType() const
Definition: SkSLType.h:404
SKSL_INT convertArraySize(const Context &context, Position arrayPos, std::unique_ptr< Expression > size) const
bool matches(const Type &other) const
Definition: SkSLType.h:269
virtual bool isMatrix() const
Definition: SkSLType.h:528
virtual int columns() const
Definition: SkSLType.h:429
static constexpr int kUnsizedArray
Definition: SkSLType.h:99
const Type & columnType(const Context &context) const
Definition: SkSLType.h:411
std::string displayName() const
Definition: SkSLType.h:234
uint8_t value
bool HasSideEffects(const Expression &expr)
static bool index_out_of_range(const Context &context, Position pos, SKSL_INT index, const Expression &base)
OperatorPrecedence
Definition: SkSLOperator.h:57
static SkString to_string(int n)
Definition: nanobench.cpp:119