diff --git a/.gitignore b/.gitignore index a450ff48d9572d4a09ee1b3d0ff20a04065634a5..671de6ecf2cbbbfc1ddf5340e225a4b54ee895b9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ build Documentations/1-parser/*.pdf compile_commands.json .cache +.vscode todo.txt tmp.cminus diff --git a/include/optimization/GVN.h b/include/optimization/GVN.h index 144ddfce0fe1a7f7466721d33eef0b7630ba6d84..9e0a82caf4ed5090557914e790a7165df3c46fc1 100644 --- a/include/optimization/GVN.h +++ b/include/optimization/GVN.h @@ -204,13 +204,15 @@ class UniqueExpression : public Expression { } virtual std::string print() { return "(UNIQUE " + instr_->print() + ")"; } - bool equiv(const UniqueExpression *other) const { return false; } + bool equiv(const UniqueExpression *other) const { + return instr_ == other->instr_; + } UniqueExpression(Instruction *instr) : Expression(e_unique), instr_(instr) {} private: - std::shared_ptr instr_; + Instruction *instr_; }; } // namespace GVNExpression @@ -277,7 +279,7 @@ class GVN : public Pass { BasicBlock *bb); std::shared_ptr valueExpr( Instruction *instr, - partitions *part = nullptr); + const partitions &part); std::shared_ptr getVN( const partitions &pout, std::shared_ptr ve); @@ -293,11 +295,6 @@ class GVN : public Pass { return std::make_shared(index); } - // self add - // - std::uint64_t new_number() { return next_value_number_++; } - static int pretend_copy_stmt(Instruction *inst, BasicBlock *bb); - private: bool dump_json_; std::uint64_t next_value_number_ = 1; @@ -307,10 +304,24 @@ class GVN : public Pass { std::unique_ptr folder_; std::unique_ptr dce_; - // self add + // self add member std::map _TOP; partitions join_helper(BasicBlock *pre1, BasicBlock *pre2); - BasicBlock* curr_bb; + BasicBlock *curr_bb; + // + // self add function + // + std::uint64_t new_number() { return next_value_number_++; } + static int pretend_copy_stmt(Instruction *inst, BasicBlock *bb); + std::shared_ptr search_ve( + Value *v, + const partitions &part); + + std::vector> core_( + Instruction *instr, + const partitions &part, + size_t count, + bool fold_ = true); }; bool operator==(const GVN::partitions &p1, const GVN::partitions &p2); diff --git a/src/optimization/GVN.cpp b/src/optimization/GVN.cpp index db437dc140b4feb1293064570958229f68c46307..aee69a63ca5cbdc5e1521cfc1dd7734ecc010cc1 100644 --- a/src/optimization/GVN.cpp +++ b/src/optimization/GVN.cpp @@ -235,9 +235,8 @@ GVN::join(const partitions &P1, const partitions &P2) { return P; } -std::shared_ptr -GVN::intersect(std::shared_ptr ci, - std::shared_ptr cj) { +shared_ptr +GVN::intersect(shared_ptr ci, shared_ptr cj) { // TODO auto c = createCongruenceClass(); std::set intersection; @@ -256,15 +255,17 @@ GVN::intersect(std::shared_ptr ci, *ci->value_phi_ == *cj->value_phi_) c->value_phi_ = ci->value_phi_; - // if (c->members_.size() or c->value_expr_ or c->value_phi_) // not empty // ?? // What if the ve is nullptr? if (c->members_.size()) // not empty + { if (c->index_ == 0) { c->index_ = new_number(); - c->value_phi_ = + c->value_expr_ = c->value_phi_ = PhiExpression::create(ci->value_expr_, cj->value_expr_); } + c->leader_ = *c->members_.begin(); + } return c; } @@ -276,7 +277,7 @@ GVN::detectEquivalences() { for (auto &bb : func_->get_basic_blocks()) { for (auto &instr : bb.get_instructions()) std::cout << &instr << "\t" << instr.print() << std::endl; - } + } // initialize pout with top for (auto &bb : func_->get_basic_blocks()) { @@ -329,87 +330,119 @@ GVN::detectEquivalences() { } while (changed); } +// if v is already in one congruence set, return that value_expr_ +// or return nullptr shared_ptr -GVN::valueExpr(Instruction *instr, partitions *part) { +GVN::search_ve(Value *v, const GVN::partitions &part) { + for (auto c : part) { + if (std::find(c->members_.begin(), c->members_.end(), v) != + c->members_.end()) { + return c->value_expr_; + } + } + return nullptr; +} + +// for each op, try to find the +std::vector> +GVN::core_(Instruction *instr, + const partitions &part, + size_t count, + bool fold_) { + assert(not(fold_ and count > 2)); + + Value *v; + Constant *v_const; + auto operands = instr->get_operands(); + std::vector> ret; + + // if able to fold, then fold + fold_ &= bool(dynamic_cast(operands[0])); + if (count == 2) + fold_ &= bool(dynamic_cast(operands[1])); + if (fold_) { + Constant *res; + if (count == 1) { + res = + folder_->compute(instr, dynamic_cast(operands[0])); + } else { + // count == 2 + res = folder_->compute(instr, + dynamic_cast(operands[0]), + dynamic_cast(operands[1])); + } + ret.push_back(ConstantExpression::create(res)); + return ret; + } + + // normal case: + // - try to find expression that already exists + // - take care of constant + for (int i = 0; i != count; i++) { + v = operands[i]; + v_const = dynamic_cast(v); + if (v_const) { + ret.push_back(ConstantExpression::create(v_const)); + } else { + auto res = search_ve(v, part); + assert(res); + ret.push_back(res); + } + } + return ret; +} + +shared_ptr +GVN::valueExpr(Instruction *instr, const partitions &part) { // TODO // ?? should use part? std::string err{"Undefined"}; - std::cout << instr->print() << std::endl; + std::vector> res; + + // first check if there is already one congruence class inside + auto tmp = search_ve(instr, part); + if (tmp) + return tmp; if (instr->isBinary() or instr->is_cmp() or instr->is_fcmp()) { - auto op1 = instr->get_operand(0); - auto op2 = instr->get_operand(1); - auto op1_const = dynamic_cast(op1); - auto op2_const = dynamic_cast(op2); - if (op1_const and op2_const) { - // both are constant number, so: - // constant fold! - return ConstantExpression::create( - folder_->compute(instr, op1_const, op2_const)); - } else { - // both none constant - auto op1_instr = dynamic_cast(op1); - auto op2_instr = dynamic_cast(op2); - assert((op1_instr or op1_const) and (op2_instr or op2_const) && - "must be this case"); + res = core_(instr, part, 2); + if (res.size() == 1) // constant fold + return res[0]; + else return BinaryExpression::create( - instr->get_instr_type(), - (op1_const ? ConstantExpression::create(op1_const) - : valueExpr(op1_instr)), - (op2_const ? ConstantExpression::create(op2_const) - : valueExpr(op2_instr))); - } + instr->get_instr_type(), res[0], res[1]); } else if (instr->is_phi()) { err = "phi"; } else if (instr->is_fp2si() or instr->is_si2fp() or instr->is_zext()) { - auto op = instr->get_operand(0); - auto op_const = dynamic_cast(op); - auto op_instr = dynamic_cast(op); - assert(op_instr or op_const); - - // get dest type - auto instr_fp2si = dynamic_cast(instr); - auto instr_si2fp = dynamic_cast(instr); - auto instr_zext = dynamic_cast(instr); - Type *dest_type = nullptr; - if (instr_fp2si) - dest_type = instr_fp2si->get_dest_type(); - else if (instr_si2fp) - dest_type = instr_si2fp->get_dest_type(); - else if (instr_zext) - dest_type = instr_zext->get_dest_type(); - else - err = "cast"; + res = core_(instr, part, 1); + if (res[0]->get_expr_type() == Expression::e_constant) + return res[0]; + Type *dest_type; + + switch (instr->get_instr_type()) { + case Instruction::fptosi: + dest_type = static_cast(instr)->get_dest_type(); + break; + case Instruction::sitofp: + dest_type = static_cast(instr)->get_dest_type(); + break; + case Instruction::zext: + dest_type = static_cast(instr)->get_dest_type(); + break; + default: + dest_type = nullptr; + err = "cast"; + } if (dest_type) { - if (op_const) - return ConstantExpression::create( - folder_->compute(instr, op_const)); - else - return CastExpression::create( - instr->get_instr_type(), valueExpr(op_instr), dest_type); + return CastExpression::create( + instr->get_instr_type(), res[0], dest_type); } } else if (instr->is_gep()) { - auto operands = instr->get_operands(); - auto ptr = operands[0]; - std::vector> idxs; - // check for base address - assert(not dynamic_cast(ptr) and - dynamic_cast(ptr) && - "base address should only be from instruction"); - // set idxes - for (int i = 1; i < operands.size(); i++) { - if (dynamic_cast(operands[i])) - idxs.push_back(ConstantExpression::create( - dynamic_cast(operands[i]))); - else { - assert(dynamic_cast(operands[i])); - idxs.push_back( - valueExpr(dynamic_cast(operands[i]))); - } - } - return GEPExpression::create( - valueExpr(dynamic_cast(ptr)), idxs); + res = core_(instr, part, instr->get_operands().size(), false); + auto ptr = res[0]; + res.erase(res.begin()); + return GEPExpression::create(ptr, res); } else if (instr->is_load() or instr->is_alloca() or instr->is_call()) { return UniqueExpression::create(instr); } @@ -450,31 +483,26 @@ GVN::transferFunction(Instruction *x, Value *e, partitions pin) { if (e_const) ve = ConstantExpression::create(e_const); else - ve = valueExpr(e_instr, &pin); + ve = valueExpr(e_instr, pin); } else - ve = valueExpr(x, &pin); + ve = valueExpr(x, pin); auto vpf = valuePhiFunc(ve, curr_bb); + // TODO: set leader for (auto c : pout) { - if (ve == c->value_expr_ or (vpf and vpf == c->value_phi_)) { + if (ve == c->value_expr_ or + (vpf and c->value_phi_ and *vpf == *c->value_phi_)) { c->value_expr_ = ve; c->members_.insert(x); - } else { - auto c = createCongruenceClass(new_number()); - c->members_.insert(x); - c->value_expr_ = ve; - c->value_phi_ = vpf; - pout.insert(c); + return pout; } } - - /* // first version: ignore ve and vpf - * // and only update index, leader and members - * auto c = createCongruenceClass(new_number()); - * c->leader_ = x; - * c->members_.insert(x); - * pout.insert(c); */ - + auto c = createCongruenceClass(new_number()); + c->members_.insert(x); + c->leader_ = x; + c->value_expr_ = ve; + c->value_phi_ = vpf; + pout.insert(c); return pout; } @@ -491,7 +519,9 @@ GVN::transferFunction(BasicBlock *bb) { * LOG_INFO << "pin:\n"; * utils::print_partitions(pin_[bb]); * LOG_INFO << "pout before:\n"; - * utils::print_partitions(pout_[bb]); */ + * utils::print_partitions(pout_[bb]); */ + std::cout << "for basic block " << bb->get_name() << ", pin:" << std::endl; + utils::print_partitions(pin_[bb]); // iterate through all instructions in the block for (auto &instr : bb->get_instructions()) { @@ -503,7 +533,7 @@ GVN::transferFunction(BasicBlock *bb) { for (auto succ : bb->get_succ_basic_blocks()) { for (auto &instr : succ->get_instructions()) { if (instr.is_phi()) { - if ((res = pretend_copy_stmt(&instr, bb) == -1)) + if ((res = pretend_copy_stmt(&instr, bb)) == -1) continue; part = transferFunction(&instr, instr.get_operand(res), part); } @@ -572,19 +602,19 @@ GVN::initPerFunction() { void GVN::replace_cc_members() { for (auto &[_bb, part] : pout_) { - auto bb = - _bb; // workaround: structured bindings can't be captured in C++17 + auto bb = _bb; // workaround: structured bindings can't be captured + // in C++17 for (auto &cc : part) { if (cc->index_ == 0) continue; - // if you are planning to do constant propagation, leaders should be - // set to constant at some point + // if you are planning to do constant propagation, leaders + // should be set to constant at some point for (auto &member : cc->members_) { bool member_is_phi = dynamic_cast(member); bool value_phi = cc->value_phi_ != nullptr; if (member != cc->leader_ and (value_phi or !member_is_phi)) { - // only replace the members if users are in the same block - // as bb + // only replace the members if users are in the same + // block as bb member->replace_use_with_when( cc->leader_, [bb](User *user) { if (auto instr = @@ -592,7 +622,7 @@ GVN::replace_cc_members() { auto parent = instr->get_parent(); auto &bb_pre = parent->get_pre_basic_blocks(); if (instr->is_phi()) // as copy stmt, the phi - // belongs to this block + // belongs to this block return std::find(bb_pre.begin(), bb_pre.end(), bb) != bb_pre.end(); @@ -650,7 +680,8 @@ GVN::run() { gvn_json << "\n\"pout\": " << utils::dump_bb2partition(pout_); gvn_json << "},"; } - replace_cc_members(); // don't delete instructions, just replace them + replace_cc_members(); // don't delete instructions, just replace + // them } dce_->run(); // let dce do that for us if (dump_json_) diff --git a/tests/4-ir-opt/testcases/GVN/functional/clear.sh b/tests/4-ir-opt/testcases/GVN/functional/clear.sh new file mode 100755 index 0000000000000000000000000000000000000000..ddad120f63075b1c150ebe7fbe5425d96a901420 --- /dev/null +++ b/tests/4-ir-opt/testcases/GVN/functional/clear.sh @@ -0,0 +1,4 @@ +rm -rf *.ll +rm -rf gvn.json +rm -rf `ls | grep -v "\."` + diff --git a/tests/4-ir-opt/testcases/GVN/performance/clear.sh b/tests/4-ir-opt/testcases/GVN/performance/clear.sh new file mode 100755 index 0000000000000000000000000000000000000000..9c4fa19eeda57c8d7c30f0ba87ede34af73b7991 --- /dev/null +++ b/tests/4-ir-opt/testcases/GVN/performance/clear.sh @@ -0,0 +1,3 @@ +rm -rf const-prop +rm -rf transpose +