// Constant propagation with conditional branches,
// Wegman and Zadeck, ACM TOPLAS 13(2):181-210.
#include "ccp_pass.h"
-
#include "fold.h"
#include "function.h"
#include "module.h"
#include "propagator.h"
+#include <algorithm>
+
namespace spvtools {
namespace opt {
// Otherwise, see if the RHS of the assignment folds into a constant value.
std::vector<uint32_t> cst_val_ids;
- for (uint32_t i = 0; i < instr->NumInOperands(); i++) {
- uint32_t op_id = instr->GetSingleWordInOperand(i);
- auto it = values_.find(op_id);
- if (it != values_.end()) {
- cst_val_ids.push_back(it->second);
- } else {
- break;
+ bool missing_constants = false;
+ instr->ForEachInId([this, &cst_val_ids, &missing_constants](uint32_t* op_id) {
+ auto it = values_.find(*op_id);
+ if (it == values_.end()) {
+ missing_constants = 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.
- auto constants = const_mgr_->GetConstantsFromIds(cst_val_ids);
- if (constants.size() == 0) {
+ if (missing_constants) {
return SSAPropagator::kNotInteresting;
}
+ 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 SSAPropagator::kVarying;
+ }
+
// Otherwise, fold the instruction with all the operands to produce a new
// constant.
uint32_t result_val = FoldScalars(instr->opcode(), constants);
SSAPropagator::PropStatus CCPPass::VisitBranch(ir::Instruction* instr,
ir::BasicBlock** dest_bb) const {
assert(instr->IsBranch() && "Expected a branch instruction.");
- uint32_t dest_label = 0;
+ *dest_bb = nullptr;
+ uint32_t dest_label = 0;
if (instr->opcode() == SpvOpBranch) {
// An unconditional jump always goes to its unique destination.
dest_label = instr->GetSingleWordInOperand(0);
auto it = values_.find(pred_id);
if (it == values_.end()) {
// The predicate has an unknown value, either branch could be taken.
- *dest_bb = nullptr;
return SSAPropagator::kVarying;
}
// which of the target literals it matches. The branch associated with that
// literal is the taken branch.
assert(instr->opcode() == SpvOpSwitch);
+ if (instr->GetOperand(0).words.size() != 1) {
+ // If the selector is wider than 32-bits, return varying. TODO(dnovillo):
+ // Add support for wider constants.
+ return SSAPropagator::kVarying;
+ }
uint32_t select_id = instr->GetSingleWordOperand(0);
auto it = values_.find(select_id);
if (it == values_.end()) {
// The selector has an unknown value, any of the branches could be taken.
- *dest_bb = nullptr;
return SSAPropagator::kVarying;
}
const Type* type, const std::vector<uint32_t>& literal_words_or_ids);
// Gets or creates a Constant instance to hold the constant value of the given
- // instruction. It returns a pointer to the Constant's defining instruction or
- // nullptr if it could not create the constant.
+ // instruction. It returns a pointer to a Constant instance or nullptr if it
+ // could not create the constant.
const Constant* GetConstantFromInst(ir::Instruction* inst);
// Gets or creates a constant defining instruction for the given Constant |c|.
// not accepted in this function.
uint32_t FoldScalars(SpvOp opcode,
const std::vector<const analysis::Constant*>& operands) {
+ assert(IsFoldableOpcode(opcode) &&
+ "Unhandled instruction opcode in FoldScalars");
std::vector<uint32_t> operand_values_in_raw_words;
for (const auto& operand : operands) {
if (const analysis::ScalarConstant* scalar = operand->AsScalarConstant()) {
return OperateWords(opcode, operand_values_in_raw_words);
}
-// Returns the result of performing an operation over constant vectors. 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.
std::vector<uint32_t> FoldVectors(
SpvOp opcode, uint32_t num_dims,
const std::vector<const analysis::Constant*>& operands) {
+ assert(IsFoldableOpcode(opcode) &&
+ "Unhandled instruction opcode in FoldVectors");
std::vector<uint32_t> result;
for (uint32_t d = 0; d < num_dims; d++) {
std::vector<uint32_t> operand_values_for_one_dimension;
}
}
+bool IsFoldableConstant(const analysis::Constant* cst) {
+ // Currently supported constants are 32-bit values or null constants.
+ if (const analysis::ScalarConstant* scalar = cst->AsScalarConstant())
+ return scalar->words().size() == 1;
+ else
+ return cst->AsNullConstant() != nullptr;
+}
+
} // namespace opt
} // namespace spvtools
namespace spvtools {
namespace opt {
+// 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
+// faill with a call to assert.
uint32_t FoldScalars(SpvOp opcode,
const std::vector<const analysis::Constant*>& operands);
+// 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
+// faill with a call to assert.
std::vector<uint32_t> FoldVectors(
SpvOp opcode, uint32_t num_dims,
const std::vector<const analysis::Constant*>& operands);
+// Returns true if |opcode| represents an operation handled by FoldScalars or
+// FoldVectors.
bool IsFoldableOpcode(SpvOp opcode);
+// Returns true if |cst| is supported by FoldScalars and FoldVectors.
+bool IsFoldableConstant(const analysis::Constant* cst);
+
} // namespace opt
} // namespace spvtools
bool Run(ir::Function* fn);
// Returns true if the |i|th argument for |phi| comes through a CFG edge that
- // has been marked executable.
+ // has been marked executable. |i| should be an index value accepted by
+ // Instruction::GetSingleWordOperand.
bool IsPhiArgExecutable(ir::Instruction* phi, uint32_t i) const;
private: