summaryrefslogtreecommitdiff
path: root/source/opt/fold.h
blob: c4e0dbc20ee28c90bd6f7f387a1423e59f0ed2e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright (c) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef LIBSPIRV_UTIL_FOLD_H_
#define LIBSPIRV_UTIL_FOLD_H_

#include <cstdint>
#include <vector>

#include "const_folding_rules.h"
#include "constants.h"
#include "def_use_manager.h"
#include "folding_rules.h"

namespace spvtools {
namespace opt {

class InstructionFolder {
 public:
  // Returns the result of folding a scalar instruction with the given |opcode|
  // and |operands|. Each entry in |operands| is a pointer to an
  // analysis::Constant instance, which should've been created with the constant
  // manager (See IRContext::get_constant_mgr).
  //
  // It is an error to call this function with an opcode that does not pass the
  // IsFoldableOpcode test. If any error occurs during folding, the folder will
  // fail with a call to assert.
  uint32_t FoldScalars(
      SpvOp opcode,
      const std::vector<const analysis::Constant*>& operands) const;

  // Returns the result of performing an operation with the given |opcode| over
  // constant vectors with |num_dims| dimensions.  Each entry in |operands| is a
  // pointer to an analysis::Constant instance, which should've been created
  // with the constant manager (See IRContext::get_constant_mgr).
  //
  // This function iterates through the given vector type constant operands and
  // calculates the result for each element of the result vector to return.
  // Vectors with longer than 32-bit scalar components are not accepted in this
  // function.
  //
  // It is an error to call this function with an opcode that does not pass the
  // IsFoldableOpcode test. If any error occurs during folding, the folder will
  // fail with a call to assert.
  std::vector<uint32_t> FoldVectors(
      SpvOp opcode, uint32_t num_dims,
      const std::vector<const analysis::Constant*>& operands) const;

  // Returns true if |opcode| represents an operation handled by FoldScalars or
  // FoldVectors.
  bool IsFoldableOpcode(SpvOp opcode) const;

  // Returns true if |cst| is supported by FoldScalars and FoldVectors.
  bool IsFoldableConstant(const analysis::Constant* cst) const;

  // Returns true if |FoldInstructionToConstant| could fold an instruction whose
  // result type is |type_inst|.
  bool IsFoldableType(opt::Instruction* type_inst) const;

  // Tries to fold |inst| to a single constant, when the input ids to |inst|
  // have been substituted using |id_map|.  Returns a pointer to the OpConstant*
  // instruction if successful.  If necessary, a new constant instruction is
  // created and placed in the global values section.
  //
  // |id_map| is a function that takes one result id and returns another.  It
  // can be used for things like CCP where it is known that some ids contain a
  // constant, but the instruction itself has not been updated yet.  This can
  // map those ids to the appropriate constants.
  opt::Instruction* FoldInstructionToConstant(
      opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const;
  // Returns true if |inst| can be folded into a simpler instruction.
  // If |inst| can be simplified, |inst| is overwritten with the simplified
  // instruction reusing the same result id.
  //
  // If |inst| is simplified, it is possible that the resulting code in invalid
  // because the instruction is in a bad location.  Callers of this function
  // have to handle the following cases:
  //
  // 1) An OpPhi becomes and OpCopyObject - If there are OpPhi instruction after
  //    |inst| in a basic block then this is invalid.  The caller must fix this
  //    up.
  bool FoldInstruction(opt::Instruction* inst) const;

  // Return true if this opcode has a const folding rule associtated with it.
  bool HasConstFoldingRule(SpvOp opcode) const {
    return GetConstantFoldingRules().HasFoldingRule(opcode);
  }

 private:
  // Returns a reference to the ConstnatFoldingRules instance.
  const ConstantFoldingRules& GetConstantFoldingRules() const {
    return const_folding_rules;
  }

  // Returns a reference to the FoldingRules instance.
  const FoldingRules& GetFoldingRules() const { return folding_rules; }

  // Returns the single-word result from performing the given unary operation on
  // the operand value which is passed in as a 32-bit word.
  uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) const;

  // Returns the single-word result from performing the given binary operation
  // on the operand values which are passed in as two 32-bit word.
  uint32_t BinaryOperate(SpvOp opcode, uint32_t a, uint32_t b) const;

  // Returns the single-word result from performing the given ternary operation
  // on the operand values which are passed in as three 32-bit word.
  uint32_t TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b,
                          uint32_t c) const;

  // Returns the single-word result from performing the given operation on the
  // operand words. This only works with 32-bit operations and uses boolean
  // convention that 0u is false, and anything else is boolean true.
  // TODO(qining): Support operands other than 32-bit wide.
  uint32_t OperateWords(SpvOp opcode,
                        const std::vector<uint32_t>& operand_words) const;

  bool FoldInstructionInternal(opt::Instruction* inst) const;

  // Returns true if |inst| is a binary operation that takes two integers as
  // parameters and folds to a constant that can be represented as an unsigned
  // 32-bit value when the ids have been replaced by |id_map|.  If |inst| can be
  // folded, the resulting value is returned in |*result|.  Valid result types
  // for the instruction are any integer (signed or unsigned) with 32-bits or
  // less, or a boolean value.
  bool FoldBinaryIntegerOpToConstant(
      Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
      uint32_t* result) const;

  // Returns true if |inst| is a binary operation on two boolean values, and
  // folds
  // to a constant boolean value when the ids have been replaced using |id_map|.
  // If |inst| can be folded, the result value is returned in |*result|.
  bool FoldBinaryBooleanOpToConstant(
      Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
      uint32_t* result) const;

  // Returns true if |inst| can be folded to an constant when the ids have been
  // substituted using id_map.  If it can, the value is returned in |result|. If
  // not, |result| is unchanged.  It is assumed that not all operands are
  // constant.  Those cases are handled by |FoldScalar|.
  bool FoldIntegerOpToConstant(Instruction* inst,
                               const std::function<uint32_t(uint32_t)>& id_map,
                               uint32_t* result) const;

  // Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|.
  ConstantFoldingRules const_folding_rules;

  // Folding rules used by |FoldInstruction|.
  FoldingRules folding_rules;
};

}  // namespace opt
}  // namespace spvtools

#endif  // LIBSPIRV_UTIL_FOLD_H_