return MarkInstructionVarying(instr);
}
- // Otherwise, see if the RHS of the assignment folds into a constant value.
- std::vector<uint32_t> 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,
#include "fold.h"
#include "def_use_manager.h"
+#include "ir_context.h"
#include <cassert>
#include <vector>
return cst->AsNullConstant() != nullptr;
}
+ir::Instruction* FoldInstructionToConstant(
+ ir::Instruction* inst, std::function<uint32_t(uint32_t)> 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<const analysis::Constant*> 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<uint32_t(uint32_t)> 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
// 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<uint32_t(uint32_t)> 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<uint32_t(uint32_t)> 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
}
}
-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
Instruction* InsertBefore(std::unique_ptr<Instruction>&& i);
using utils::IntrusiveNodeBase<Instruction>::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 {
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