From: Steven Perron Date: Tue, 9 Jan 2018 17:45:46 +0000 (-0500) Subject: Add generic folding function and use in CCP X-Git-Tag: upstream/2018.6~570 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1ebd860daabf3328715d010af39f4950ec26d9c6;p=platform%2Fupstream%2FSPIRV-Tools.git Add generic folding function and use in CCP The current folding routines have a very cumbersome interface, make them harder to use, and not a obvious how to extend. This change is to create a new interface for the folding routines, and show how it can be used by calling it from CCP. This does not make a significant change to the behaviour of CCP. In general it should produce the same code as before; however it is possible that an instruction that takes 32-bit integers as inputs and the result is not a 32-bit integer or bool will not be folded as before. --- diff --git a/source/opt/ccp_pass.cpp b/source/opt/ccp_pass.cpp index dc8aecd..ed633ca 100644 --- a/source/opt/ccp_pass.cpp +++ b/source/opt/ccp_pass.cpp @@ -120,58 +120,41 @@ SSAPropagator::PropStatus CCPPass::VisitAssignment(ir::Instruction* instr) { return MarkInstructionVarying(instr); } - // Otherwise, see if the RHS of the assignment folds into a constant value. - std::vector cst_val_ids; - bool missing_constants = false; - bool varying_values = false; - instr->ForEachInId([this, &cst_val_ids, &missing_constants, - &varying_values](uint32_t* op_id) { + // See if the RHS of the assignment folds into a constant value. + auto map_func = [this](uint32_t id) { + auto it = values_.find(id); + if (it == values_.end() || IsVaryingValue(it->second)) { + return id; + } + return it->second; + }; + ir::Instruction* folded_inst = + opt::FoldInstructionToConstant(instr, map_func); + if (folded_inst != nullptr) { + // We do not want to change the body of the function by adding new + // instructions. When folding we can only generate new constants. + assert(folded_inst->IsConstant() && "CCP is only interested in constant."); + values_[instr->result_id()] = folded_inst->result_id(); + return SSAPropagator::kInteresting; + } + + // If not, see if there is a least one unknown operand to the instruction. If + // so, we might be able to fold it later. + bool could_be_improved = false; + instr->ForEachInId([this, &could_be_improved](uint32_t* op_id) { auto it = values_.find(*op_id); if (it == values_.end()) { - missing_constants = true; - return; - } else if (IsVaryingValue(it->second)) { - varying_values = true; + could_be_improved = true; return; } - cst_val_ids.push_back(it->second); }); - - // If we did not find a constant value for every operand in the instruction, - // do not bother folding it. Indicate that this instruction does not produce - // an interesting value for now. - if (missing_constants) { + if (could_be_improved) { return SSAPropagator::kNotInteresting; } - // If we found at least one varying value, the instruction will never fold - // into anything interesting. Mark it varying. - if (varying_values) { - return MarkInstructionVarying(instr); - } - - auto constants = const_mgr_->GetConstantsFromIds(cst_val_ids); - assert(constants.size() != 0 && "Found undeclared constants"); - - // If any of the constants are not supported by the folder, we will not be - // able to produce a constant out of this instruction. Consider it varying - // in that case. - if (!std::all_of(constants.begin(), constants.end(), - [](const analysis::Constant* cst) { - return IsFoldableConstant(cst); - })) { - return MarkInstructionVarying(instr); - } - - // Otherwise, fold the instruction with all the operands to produce a new - // constant. - uint32_t result_val = FoldScalars(instr->opcode(), constants); - const analysis::Constant* result_const = - const_mgr_->GetConstant(const_mgr_->GetType(instr), {result_val}); - ir::Instruction* const_decl = - const_mgr_->GetDefiningInstruction(result_const); - values_[instr->result_id()] = const_decl->result_id(); - return SSAPropagator::kInteresting; + // Otherwise, we will never be able to fold this instruction, so mark it + // varying. + return MarkInstructionVarying(instr); } SSAPropagator::PropStatus CCPPass::VisitBranch(ir::Instruction* instr, diff --git a/source/opt/fold.cpp b/source/opt/fold.cpp index 96f6640..97314d4 100644 --- a/source/opt/fold.cpp +++ b/source/opt/fold.cpp @@ -14,6 +14,7 @@ #include "fold.h" #include "def_use_manager.h" +#include "ir_context.h" #include #include @@ -289,5 +290,65 @@ bool IsFoldableConstant(const analysis::Constant* cst) { return cst->AsNullConstant() != nullptr; } +ir::Instruction* FoldInstructionToConstant( + ir::Instruction* inst, std::function id_map) { + if (!inst->IsFoldable()) { + return nullptr; + } + + ir::IRContext* context = inst->context(); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + // Collect the values of the constant parameters. + std::vector constants; + bool missing_constants = false; + inst->ForEachInId([&constants, &missing_constants, const_mgr, + &id_map](uint32_t* op_id) { + uint32_t id = id_map(*op_id); + const analysis::Constant* const_op = const_mgr->FindDeclaredConstant(id); + if (!const_op || !IsFoldableConstant(const_op)) { + constants.push_back(nullptr); + missing_constants = true; + return; + } + constants.push_back(const_op); + }); + + // If all parameters are constant, fold the instruction to a constant. + if (!missing_constants) { + uint32_t result_val = FoldScalars(inst->opcode(), constants); + const analysis::Constant* result_const = + const_mgr->GetConstant(const_mgr->GetType(inst), {result_val}); + return const_mgr->GetDefiningInstruction(result_const); + } + + // TODO: Add other folding opportunities that will generate a constant. + return nullptr; +} + +bool IsFoldableType(ir::Instruction* type_inst) { + // Support 32-bit integers. + if (type_inst->opcode() == SpvOpTypeInt) { + return type_inst->GetSingleWordInOperand(0) == 32; + } + // Support booleans. + if (type_inst->opcode() == SpvOpTypeBool) { + return true; + } + // Nothing else yet. + return false; +} +ir::Instruction* FoldInstruction(ir::Instruction* inst, + std::function id_map) { + ir::Instruction* folded_inst = FoldInstructionToConstant(inst, id_map); + if (folded_inst != nullptr) { + return folded_inst; + } + + // TODO: Add other folding opportunities that do not necessarily fold to a + // constant. + return nullptr; +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/fold.h b/source/opt/fold.h index 52954a5..32a083b 100644 --- a/source/opt/fold.h +++ b/source/opt/fold.h @@ -59,6 +59,43 @@ bool IsFoldableOpcode(SpvOp opcode); // Returns true if |cst| is supported by FoldScalars and FoldVectors. bool IsFoldableConstant(const analysis::Constant* cst); +// Returns true if |FoldInstructionToConstant| could fold an instruction whose +// result type is |type_inst|. +bool IsFoldableType(ir::Instruction* type_inst); + +// 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. +ir::Instruction* FoldInstructionToConstant( + ir::Instruction* inst, std::function id_map); + +// Tries to fold |inst| to a simpler instruction that computes the same value, +// when the input ids to |inst| have been substituted using |id_map|. Returns a +// pointer to the simplified instruction if successful. If necessary, a new +// instruction is created and placed in the global values section, for +// constants, or after |inst| for other instructions. +// +// |inst| must be an instruction that exists in the body of a function. +// +// |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. +ir::Instruction* FoldInstruction(ir::Instruction* inst, + std::function id_map); + +// The same as above when |id_map| is the identity function. +inline ir::Instruction* FoldInstruction(ir::Instruction* inst) { + auto identity_map = [](uint32_t id) { return id; }; + return FoldInstructionToConstant(inst, identity_map); +} + } // namespace opt } // namespace spvtools diff --git a/source/opt/instruction.cpp b/source/opt/instruction.cpp index 45b7d2f..2b83ca1 100644 --- a/source/opt/instruction.cpp +++ b/source/opt/instruction.cpp @@ -468,7 +468,13 @@ bool Instruction::IsOpaqueType() const { } } -bool Instruction::IsFoldable() const { return opt::IsFoldableOpcode(opcode()); } +bool Instruction::IsFoldable() const { + if (!opt::IsFoldableOpcode(opcode())) { + return false; + } + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); + return opt::IsFoldableType(type); +} } // namespace ir } // namespace spvtools diff --git a/source/opt/instruction.h b/source/opt/instruction.h index 49836cd..61de1f9 100644 --- a/source/opt/instruction.h +++ b/source/opt/instruction.h @@ -347,6 +347,10 @@ class Instruction : public utils::IntrusiveNodeBase { Instruction* InsertBefore(std::unique_ptr&& i); using utils::IntrusiveNodeBase::InsertBefore; + // Returns true if |this| is an instruction defining a constant, but not a + // Spec constant. + inline bool IsConstant() const; + private: // Returns the total count of result type id and result id. uint32_t TypeResultIdCount() const { @@ -545,6 +549,11 @@ bool Instruction::IsDecoration() const { bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); } bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); } + +bool Instruction::IsConstant() const { + return spvOpcodeIsConstant(opcode()) && + !spvOpcodeIsScalarSpecConstant(opcode()); +} } // namespace ir } // namespace spvtools