Home | History | Annotate | Download | only in opt
      1 // Copyright (c) 2017 Google Inc.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #ifndef SOURCE_OPT_FOLD_H_
     16 #define SOURCE_OPT_FOLD_H_
     17 
     18 #include <cstdint>
     19 #include <vector>
     20 
     21 #include "source/opt/const_folding_rules.h"
     22 #include "source/opt/constants.h"
     23 #include "source/opt/def_use_manager.h"
     24 #include "source/opt/folding_rules.h"
     25 
     26 namespace spvtools {
     27 namespace opt {
     28 
     29 class InstructionFolder {
     30  public:
     31   explicit InstructionFolder(IRContext* context) : context_(context) {}
     32 
     33   // Returns the result of folding a scalar instruction with the given |opcode|
     34   // and |operands|. Each entry in |operands| is a pointer to an
     35   // analysis::Constant instance, which should've been created with the constant
     36   // manager (See IRContext::get_constant_mgr).
     37   //
     38   // It is an error to call this function with an opcode that does not pass the
     39   // IsFoldableOpcode test. If any error occurs during folding, the folder will
     40   // fail with a call to assert.
     41   uint32_t FoldScalars(
     42       SpvOp opcode,
     43       const std::vector<const analysis::Constant*>& operands) const;
     44 
     45   // Returns the result of performing an operation with the given |opcode| over
     46   // constant vectors with |num_dims| dimensions.  Each entry in |operands| is a
     47   // pointer to an analysis::Constant instance, which should've been created
     48   // with the constant manager (See IRContext::get_constant_mgr).
     49   //
     50   // This function iterates through the given vector type constant operands and
     51   // calculates the result for each element of the result vector to return.
     52   // Vectors with longer than 32-bit scalar components are not accepted in this
     53   // function.
     54   //
     55   // It is an error to call this function with an opcode that does not pass the
     56   // IsFoldableOpcode test. If any error occurs during folding, the folder will
     57   // fail with a call to assert.
     58   std::vector<uint32_t> FoldVectors(
     59       SpvOp opcode, uint32_t num_dims,
     60       const std::vector<const analysis::Constant*>& operands) const;
     61 
     62   // Returns true if |opcode| represents an operation handled by FoldScalars or
     63   // FoldVectors.
     64   bool IsFoldableOpcode(SpvOp opcode) const;
     65 
     66   // Returns true if |cst| is supported by FoldScalars and FoldVectors.
     67   bool IsFoldableConstant(const analysis::Constant* cst) const;
     68 
     69   // Returns true if |FoldInstructionToConstant| could fold an instruction whose
     70   // result type is |type_inst|.
     71   bool IsFoldableType(Instruction* type_inst) const;
     72 
     73   // Tries to fold |inst| to a single constant, when the input ids to |inst|
     74   // have been substituted using |id_map|.  Returns a pointer to the OpConstant*
     75   // instruction if successful.  If necessary, a new constant instruction is
     76   // created and placed in the global values section.
     77   //
     78   // |id_map| is a function that takes one result id and returns another.  It
     79   // can be used for things like CCP where it is known that some ids contain a
     80   // constant, but the instruction itself has not been updated yet.  This can
     81   // map those ids to the appropriate constants.
     82   Instruction* FoldInstructionToConstant(
     83       Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const;
     84   // Returns true if |inst| can be folded into a simpler instruction.
     85   // If |inst| can be simplified, |inst| is overwritten with the simplified
     86   // instruction reusing the same result id.
     87   //
     88   // If |inst| is simplified, it is possible that the resulting code in invalid
     89   // because the instruction is in a bad location.  Callers of this function
     90   // have to handle the following cases:
     91   //
     92   // 1) An OpPhi becomes and OpCopyObject - If there are OpPhi instruction after
     93   //    |inst| in a basic block then this is invalid.  The caller must fix this
     94   //    up.
     95   bool FoldInstruction(Instruction* inst) const;
     96 
     97   // Return true if this opcode has a const folding rule associtated with it.
     98   bool HasConstFoldingRule(SpvOp opcode) const {
     99     return GetConstantFoldingRules().HasFoldingRule(opcode);
    100   }
    101 
    102  private:
    103   // Returns a reference to the ConstnatFoldingRules instance.
    104   const ConstantFoldingRules& GetConstantFoldingRules() const {
    105     return const_folding_rules;
    106   }
    107 
    108   // Returns a reference to the FoldingRules instance.
    109   const FoldingRules& GetFoldingRules() const { return folding_rules; }
    110 
    111   // Returns the single-word result from performing the given unary operation on
    112   // the operand value which is passed in as a 32-bit word.
    113   uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) const;
    114 
    115   // Returns the single-word result from performing the given binary operation
    116   // on the operand values which are passed in as two 32-bit word.
    117   uint32_t BinaryOperate(SpvOp opcode, uint32_t a, uint32_t b) const;
    118 
    119   // Returns the single-word result from performing the given ternary operation
    120   // on the operand values which are passed in as three 32-bit word.
    121   uint32_t TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b,
    122                           uint32_t c) const;
    123 
    124   // Returns the single-word result from performing the given operation on the
    125   // operand words. This only works with 32-bit operations and uses boolean
    126   // convention that 0u is false, and anything else is boolean true.
    127   // TODO(qining): Support operands other than 32-bit wide.
    128   uint32_t OperateWords(SpvOp opcode,
    129                         const std::vector<uint32_t>& operand_words) const;
    130 
    131   bool FoldInstructionInternal(Instruction* inst) const;
    132 
    133   // Returns true if |inst| is a binary operation that takes two integers as
    134   // parameters and folds to a constant that can be represented as an unsigned
    135   // 32-bit value when the ids have been replaced by |id_map|.  If |inst| can be
    136   // folded, the resulting value is returned in |*result|.  Valid result types
    137   // for the instruction are any integer (signed or unsigned) with 32-bits or
    138   // less, or a boolean value.
    139   bool FoldBinaryIntegerOpToConstant(
    140       Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
    141       uint32_t* result) const;
    142 
    143   // Returns true if |inst| is a binary operation on two boolean values, and
    144   // folds
    145   // to a constant boolean value when the ids have been replaced using |id_map|.
    146   // If |inst| can be folded, the result value is returned in |*result|.
    147   bool FoldBinaryBooleanOpToConstant(
    148       Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
    149       uint32_t* result) const;
    150 
    151   // Returns true if |inst| can be folded to an constant when the ids have been
    152   // substituted using id_map.  If it can, the value is returned in |result|. If
    153   // not, |result| is unchanged.  It is assumed that not all operands are
    154   // constant.  Those cases are handled by |FoldScalar|.
    155   bool FoldIntegerOpToConstant(Instruction* inst,
    156                                const std::function<uint32_t(uint32_t)>& id_map,
    157                                uint32_t* result) const;
    158 
    159   IRContext* context_;
    160 
    161   // Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|.
    162   ConstantFoldingRules const_folding_rules;
    163 
    164   // Folding rules used by |FoldInstruction|.
    165   FoldingRules folding_rules;
    166 };
    167 
    168 }  // namespace opt
    169 }  // namespace spvtools
    170 
    171 #endif  // SOURCE_OPT_FOLD_H_
    172