#pragma once #include "BasicBlock.h" #include "Constant.h" #include "DeadCode.h" #include "FuncInfo.h" #include "Function.h" #include "Instruction.h" #include "Module.h" #include "PassManager.hpp" #include "Value.h" #include #include #include #include #include #include #include #include #include namespace GVNExpression { // fold the constant value class ConstFolder { public: ConstFolder(Module *m) : module_(m) {} Constant *compute(Instruction *instr, Constant *value1, Constant *value2); Constant *compute(Instruction *instr, Constant *value1); private: Module *module_; }; /** * for constructor of class derived from `Expression`, we make it public * because `std::make_shared` needs the constructor to be publicly available, * but you should call the static factory method `create` instead the constructor itself to get the desired data */ class Expression { public: // TODO: you need to extend expression types according to testcases enum gvn_expr_t { e_constant, e_bin, e_phi }; Expression(gvn_expr_t t) : expr_type(t) {} virtual ~Expression() = default; virtual std::string print() = 0; gvn_expr_t get_expr_type() const { return expr_type; } private: gvn_expr_t expr_type; }; bool operator==(const std::shared_ptr &lhs, const std::shared_ptr &rhs); bool operator==(const GVNExpression::Expression &lhs, const GVNExpression::Expression &rhs); class ConstantExpression : public Expression { public: static std::shared_ptr create(Constant *c) { return std::make_shared(c); } virtual std::string print() { return c_->print(); } // we leverage the fact that constants in lightIR have unique addresses bool equiv(const ConstantExpression *other) const { return c_ == other->c_; } ConstantExpression(Constant *c) : Expression(e_constant), c_(c) {} private: Constant *c_; }; // arithmetic expression class BinaryExpression : public Expression { public: static std::shared_ptr create(Instruction::OpID op, std::shared_ptr lhs, std::shared_ptr rhs) { return std::make_shared(op, lhs, rhs); } virtual std::string print() { return "(" + Instruction::get_instr_op_name(op_) + " " + lhs_->print() + " " + rhs_->print() + ")"; } bool equiv(const BinaryExpression *other) const { if (op_ == other->op_ and *lhs_ == *other->lhs_ and *rhs_ == *other->rhs_) return true; else return false; } BinaryExpression(Instruction::OpID op, std::shared_ptr lhs, std::shared_ptr rhs) : Expression(e_bin), op_(op), lhs_(lhs), rhs_(rhs) {} private: Instruction::OpID op_; std::shared_ptr lhs_, rhs_; }; class PhiExpression : public Expression { public: static std::shared_ptr create(std::shared_ptr lhs, std::shared_ptr rhs) { return std::make_shared(lhs, rhs); } virtual std::string print() { return "(phi " + lhs_->print() + " " + rhs_->print() + ")"; } bool equiv(const PhiExpression *other) const { if (*lhs_ == *other->lhs_ and *rhs_ == *other->rhs_) return true; else return false; } PhiExpression(std::shared_ptr lhs, std::shared_ptr rhs) : Expression(e_phi), lhs_(lhs), rhs_(rhs) {} private: std::shared_ptr lhs_, rhs_; }; } // namespace GVNExpression /** * Congruence class in each partitions * note: for constant propagation, you might need to add other fields * and for load/store redundancy detection, you most certainly need to modify the class */ struct CongruenceClass { size_t index_; // representative of the congruence class, used to replace all the members (except itself) when analysis is done Value *leader_; // value expression in congruence class std::shared_ptr value_expr_; // value φ-function is an annotation of the congruence class std::shared_ptr value_phi_; // equivalent variables in one congruence class std::set members_; CongruenceClass(size_t index) : index_(index), leader_{}, value_expr_{}, value_phi_{}, members_{} {} bool operator<(const CongruenceClass &other) const { return this->index_ < other.index_; } bool operator==(const CongruenceClass &other) const; }; namespace std { template <> // overload std::less for std::shared_ptr, i.e. how to sort the congruence classes struct less> { bool operator()(const std::shared_ptr &a, const std::shared_ptr &b) const { // nullptrs should never appear in partitions, so we just dereference it return *a < *b; } }; } // namespace std class GVN : public Pass { public: using partitions = std::set>; GVN(Module *m, bool dump_json) : Pass(m), dump_json_(dump_json) {} // pass start void run() override; // init for pass metadata; void initPerFunction(); // fill the following functions according to Pseudocode, **you might need to add more arguments** void detectEquivalences(); partitions join(const partitions &P1, const partitions &P2); std::shared_ptr intersect(std::shared_ptr, std::shared_ptr); partitions transferFunction(Instruction *x, Value *e, partitions pin); std::shared_ptr valuePhiFunc(std::shared_ptr, const partitions &); std::shared_ptr valueExpr(Instruction *instr); std::shared_ptr getVN(const partitions &pout, std::shared_ptr ve); // replace cc members with leader void replace_cc_members(); // note: be careful when to use copy constructor or clone partitions clone(const partitions &p); // create congruence class helper std::shared_ptr createCongruenceClass(size_t index = 0) { return std::make_shared(index); } private: bool dump_json_; std::uint64_t next_value_number_ = 1; Function *func_; std::map pin_, pout_; std::unique_ptr func_info_; std::unique_ptr folder_; std::unique_ptr dce_; }; bool operator==(const GVN::partitions &p1, const GVN::partitions &p2);