in service/constant-propagation/ConstantPropagationTransform.cpp [152:469]
void try_simplify(const ConstantEnvironment& env,
const cfg::InstructionIterator& cfg_it,
const Transform::Config& config,
cfg::CFGMutation& mutation) {
auto* insn = cfg_it->insn;
auto reg_is_exact = [&env](reg_t reg, int64_t val) {
auto value = env.get(reg).maybe_get<SignedConstantDomain>();
if (!value || !value->get_constant() || *value->get_constant() != val) {
return false;
}
return true;
};
auto reg_fits_lit8 = [&env](reg_t reg) -> std::optional<int8_t> {
auto value = env.get(reg).maybe_get<SignedConstantDomain>();
if (!value || !value->get_constant()) {
return std::nullopt;
}
int64_t val = *value->get_constant();
if (val < -128 || val > 127) {
return std::nullopt;
}
return (int8_t)val;
};
auto maybe_reduce_lit8 = [&](size_t idx) -> bool {
if (!config.to_int_lit8) {
return false;
}
auto val = reg_fits_lit8(insn->src(idx));
if (!val) {
return false;
}
auto new_op = [&]() -> IROpcode {
switch (insn->opcode()) {
case OPCODE_ADD_INT:
return OPCODE_ADD_INT_LIT8;
// TODO: SUB to RSUB
case OPCODE_MUL_INT:
return OPCODE_MUL_INT_LIT8;
case OPCODE_AND_INT:
return OPCODE_AND_INT_LIT8;
case OPCODE_OR_INT:
return OPCODE_OR_INT_LIT8;
case OPCODE_XOR_INT:
return OPCODE_XOR_INT_LIT8;
default:
always_assert(false);
}
not_reached();
}();
auto repl = new IRInstruction(new_op);
repl->set_src(0, insn->src(idx == 0 ? 1 : 0));
repl->set_dest(insn->dest());
repl->set_literal(*val);
mutation.replace(cfg_it, {repl});
return true;
};
auto maybe_reduce_lit8_both = [&]() {
if (maybe_reduce_lit8(0)) {
return true;
}
if (maybe_reduce_lit8(1)) {
return true;
}
return false;
};
auto reg_fits_lit16 = [&env](reg_t reg) -> std::optional<int16_t> {
auto value = env.get(reg).maybe_get<SignedConstantDomain>();
if (!value || !value->get_constant()) {
return std::nullopt;
}
int64_t val = *value->get_constant();
if (val < -32768 || val > 32767) {
return std::nullopt;
}
return (int16_t)val;
};
auto maybe_reduce_lit16 = [&](size_t idx) -> bool {
if (!config.to_int_lit16) {
return false;
}
auto val = reg_fits_lit16(insn->src(idx));
if (!val) {
return false;
}
auto new_op = [&]() -> IROpcode {
switch (insn->opcode()) {
case OPCODE_ADD_INT:
return OPCODE_ADD_INT_LIT16;
// TODO: SUB to RSUB
case OPCODE_MUL_INT:
return OPCODE_MUL_INT_LIT16;
case OPCODE_AND_INT:
return OPCODE_AND_INT_LIT16;
case OPCODE_OR_INT:
return OPCODE_OR_INT_LIT16;
case OPCODE_XOR_INT:
return OPCODE_XOR_INT_LIT16;
default:
always_assert(false);
}
not_reached();
}();
auto repl = new IRInstruction(new_op);
repl->set_src(0, insn->src(idx == 0 ? 1 : 0));
repl->set_dest(insn->dest());
repl->set_literal(*val);
mutation.replace(cfg_it, {repl});
return true;
};
auto maybe_reduce_lit16_both = [&]() {
if (maybe_reduce_lit16(0)) {
return true;
}
if (maybe_reduce_lit16(1)) {
return true;
}
return false;
};
auto replace_with_move = [&](reg_t src_reg) {
auto* move = new IRInstruction(OPCODE_MOVE);
move->set_src(0, src_reg);
move->set_dest(insn->dest());
mutation.replace(cfg_it, {move});
};
auto replace_with_const = [&](int64_t val) {
auto* c = new IRInstruction(OPCODE_CONST);
c->set_dest(insn->dest());
c->set_literal(val);
mutation.replace(cfg_it, {c});
};
auto replace_with_neg = [&](reg_t src_reg) {
auto* neg = new IRInstruction(OPCODE_NEG_INT);
neg->set_src(0, src_reg);
neg->set_dest(insn->dest());
mutation.replace(cfg_it, {neg});
};
switch (insn->opcode()) {
// These should have been handled by PeepHole, really.
case OPCODE_ADD_INT_LIT16:
case OPCODE_ADD_INT_LIT8: {
if (insn->get_literal() == 0) {
replace_with_move(insn->src(0));
}
break;
}
case OPCODE_RSUB_INT:
case OPCODE_RSUB_INT_LIT8: {
if (insn->get_literal() == 0) {
replace_with_neg(insn->src(0));
}
break;
}
case OPCODE_MUL_INT_LIT16:
case OPCODE_MUL_INT_LIT8: {
if (insn->get_literal() == 1) {
replace_with_move(insn->src(0));
break;
}
if (insn->get_literal() == 0) {
replace_with_const(0);
break;
}
if (insn->get_literal() == -1) {
replace_with_neg(insn->src(0));
break;
}
break;
}
case OPCODE_AND_INT_LIT16:
case OPCODE_AND_INT_LIT8: {
if (insn->get_literal() == 0) {
replace_with_const(0);
break;
}
if (insn->get_literal() == -1) {
replace_with_move(insn->src(0));
break;
}
break;
}
case OPCODE_OR_INT_LIT16:
case OPCODE_OR_INT_LIT8: {
if (insn->get_literal() == 0) {
replace_with_move(insn->src(0));
break;
}
if (insn->get_literal() == -1) {
replace_with_const(-1);
break;
}
break;
}
case OPCODE_XOR_INT_LIT16:
case OPCODE_XOR_INT_LIT8: {
// TODO
break;
}
case OPCODE_SHL_INT_LIT8:
case OPCODE_USHR_INT_LIT8:
case OPCODE_SHR_INT_LIT8: {
// Can at most simplify the operand, but doesn't make much sense.
break;
}
case OPCODE_ADD_INT: {
if (reg_is_exact(insn->src(0), 0)) {
replace_with_move(insn->src(1));
} else if (reg_is_exact(insn->src(1), 0)) {
replace_with_move(insn->src(0));
} else if (maybe_reduce_lit8_both()) {
break;
} else if (maybe_reduce_lit16_both()) {
break;
}
break;
}
case OPCODE_SUB_INT: {
if (reg_is_exact(insn->src(0), 0)) {
replace_with_neg(insn->src(1));
} else if (reg_is_exact(insn->src(1), 0)) {
replace_with_move(insn->src(0));
}
break;
}
case OPCODE_MUL_INT: {
if (reg_is_exact(insn->src(0), 1)) {
replace_with_move(insn->src(1));
} else if (reg_is_exact(insn->src(1), 1)) {
replace_with_move(insn->src(0));
} else if (reg_is_exact(insn->src(0), 0) || reg_is_exact(insn->src(1), 0)) {
replace_with_const(0);
} else if (reg_is_exact(insn->src(0), -1)) {
replace_with_neg(insn->src(1));
} else if (reg_is_exact(insn->src(1), -1)) {
replace_with_neg(insn->src(0));
} else if (maybe_reduce_lit8_both()) {
break;
} else if (maybe_reduce_lit16_both()) {
break;
}
break;
}
case OPCODE_AND_INT: {
if (reg_is_exact(insn->src(0), -1)) {
replace_with_move(insn->src(1));
} else if (reg_is_exact(insn->src(1), -1)) {
replace_with_move(insn->src(0));
} else if (reg_is_exact(insn->src(0), 0) || reg_is_exact(insn->src(1), 0)) {
replace_with_const(0);
} else if (maybe_reduce_lit8_both()) {
break;
} else if (maybe_reduce_lit16_both()) {
break;
}
break;
}
case OPCODE_OR_INT: {
if (reg_is_exact(insn->src(0), 0)) {
replace_with_move(insn->src(1));
} else if (reg_is_exact(insn->src(1), 0)) {
replace_with_move(insn->src(0));
} else if (reg_is_exact(insn->src(0), -1) ||
reg_is_exact(insn->src(1), -1)) {
replace_with_const(-1);
} else if (maybe_reduce_lit8_both()) {
break;
} else if (maybe_reduce_lit16_both()) {
break;
}
break;
}
case OPCODE_XOR_INT:
if (maybe_reduce_lit8_both()) {
break;
} else if (maybe_reduce_lit16_both()) {
break;
}
break;
case OPCODE_ADD_LONG:
case OPCODE_SUB_LONG:
case OPCODE_MUL_LONG:
case OPCODE_AND_LONG:
case OPCODE_OR_LONG:
case OPCODE_XOR_LONG:
// TODO: More complicated version of the above.
break;
default:
return;
}
}