2017-10-17 23:41:37 +00:00
|
|
|
// 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>
|
|
|
|
|
2018-02-15 15:41:01 +00:00
|
|
|
#include "const_folding_rules.h"
|
2018-02-02 16:55:05 +00:00
|
|
|
#include "constants.h"
|
|
|
|
#include "def_use_manager.h"
|
2018-07-05 18:19:50 +00:00
|
|
|
#include "folding_rules.h"
|
2018-02-02 16:55:05 +00:00
|
|
|
|
2017-10-17 23:41:37 +00:00
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
|
2018-07-05 18:19:50 +00:00
|
|
|
class InstructionFolder {
|
|
|
|
public:
|
2018-07-12 13:12:23 +00:00
|
|
|
explicit InstructionFolder(IRContext* context) : context_(context) {}
|
|
|
|
|
2018-07-05 18:19:50 +00:00
|
|
|
// 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;
|
2018-02-15 15:41:01 +00:00
|
|
|
|
2018-07-05 18:19:50 +00:00
|
|
|
// 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|.
|
2018-07-09 15:32:29 +00:00
|
|
|
bool IsFoldableType(opt::Instruction* type_inst) const;
|
2018-07-05 18:19:50 +00:00
|
|
|
|
|
|
|
// 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.
|
2018-07-09 15:32:29 +00:00
|
|
|
opt::Instruction* FoldInstructionToConstant(
|
|
|
|
opt::Instruction* inst, std::function<uint32_t(uint32_t)> id_map) const;
|
2018-07-05 18:19:50 +00:00
|
|
|
// 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.
|
2018-07-09 15:32:29 +00:00
|
|
|
bool FoldInstruction(opt::Instruction* inst) const;
|
2018-07-05 18:19:50 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2018-07-09 15:32:29 +00:00
|
|
|
bool FoldInstructionInternal(opt::Instruction* inst) const;
|
2018-07-05 18:19:50 +00:00
|
|
|
|
|
|
|
// 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.
|
2018-07-10 13:18:02 +00:00
|
|
|
bool FoldBinaryIntegerOpToConstant(
|
|
|
|
Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
|
|
|
|
uint32_t* result) const;
|
2018-07-05 18:19:50 +00:00
|
|
|
|
|
|
|
// 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|.
|
2018-07-10 13:18:02 +00:00
|
|
|
bool FoldBinaryBooleanOpToConstant(
|
|
|
|
Instruction* inst, const std::function<uint32_t(uint32_t)>& id_map,
|
|
|
|
uint32_t* result) const;
|
2018-07-05 18:19:50 +00:00
|
|
|
|
|
|
|
// 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|.
|
2018-07-10 13:18:02 +00:00
|
|
|
bool FoldIntegerOpToConstant(Instruction* inst,
|
|
|
|
const std::function<uint32_t(uint32_t)>& id_map,
|
2018-07-05 18:19:50 +00:00
|
|
|
uint32_t* result) const;
|
|
|
|
|
2018-07-12 13:12:23 +00:00
|
|
|
IRContext* context_;
|
|
|
|
|
2018-07-05 18:19:50 +00:00
|
|
|
// Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|.
|
|
|
|
ConstantFoldingRules const_folding_rules;
|
|
|
|
|
|
|
|
// Folding rules used by |FoldInstruction|.
|
|
|
|
FoldingRules folding_rules;
|
|
|
|
};
|
2018-01-09 17:45:46 +00:00
|
|
|
|
2017-10-17 23:41:37 +00:00
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|
|
|
|
|
|
|
|
#endif // LIBSPIRV_UTIL_FOLD_H_
|