Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 143 additions & 54 deletions hphp/runtime/vm/jit/vasm-simplify-arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,100 +152,173 @@ bool simplify(Env& env, const movzbl& inst, Vlabel b, size_t i) {

///////////////////////////////////////////////////////////////////////////////

int is_adjacent_vptr64(const Vptr64& a, const Vptr64& b, int32_t step, int32_t min_disp, int32_t max_disp) {
int is_adjacent_vptr64(const Vptr& a, const Vptr& b, int32_t step, int32_t min_disp, int32_t max_disp,
Vptr& vptr, bool& rebase_ptr) {
const int32_t min_disp_val = a.disp < b.disp ? a.disp : b.disp;
if (a.base.isValid() && b.base.isValid() &&
!a.index.isValid() && !b.index.isValid() &&
a.base == b.base &&
a.scale == 1 && b.scale == 1 &&
a.width == b.width &&
(a.disp - b.disp == step || b.disp - a.disp == step) &&
(min_disp_val >= min_disp && min_disp_val <= max_disp && (min_disp_val % step) == 0)) {
return a.disp < b.disp ? -1 : 1;
(min_disp_val % step) == 0) {
if (min_disp_val >= min_disp && min_disp_val <= max_disp) {
rebase_ptr = false;
} else if (min_disp < 0 && vixl::is_int12(min_disp)) {
// If the offset is negative and outside the range of stp/ldp, then it's
// also outside the range of str/stur/ldr/ldur. This will force the
// offset to be materialised in a register and to then use the reg+reg
// addressing mode for the loads and stores. We'll end up with assembly
// like this:
// mov x2, -1024
// str x0, [x29, x2]
// mov x2, -1032
// str x1, [x29, x2]
// If the immediate can easily be encoded into an add or sub instruction,
// then we can rebase the pointer and it will still be worth it. We'll
// then end up with assembly like this:
// add x2, x29, -1024
// stp x0, x1, [x2]
// which still saves two instructions.
rebase_ptr = true;
} else {
// If the offset is positive we can probably just a pair of stur/ldur
// instructions, i.e.
// stur x0, [x29, 1024]
// stur x1, [x29, 1032]
// so there isn't much point in rebasing the pointer. Or if the offset
// is not a 12-bit integer then it will require more work to rebase so
// may not be worth it.
return 0;
}
if (a.disp < b.disp) {
vptr = a;
return -1;
} else {
vptr = b;
return 1;
}
}
return 0;
}

bool simplify(Env& env, const store& inst, Vlabel b, size_t i) {
bool is_valid_reg_for_storepair(Env& env, Vreg s) {
if (s.isGP())
return true;

auto const op_it = env.unit.regToConst.find(s);
return op_it != env.unit.regToConst.end() && op_it->second.kind != Vconst::Double;
}

bool simplify(Env& env, const store& inst, Vlabel b, size_t i, bool post_regalloc) {
// store{s, d}; store{s, d} --> storepair{s0, s1, d}
return if_inst<Vinstr::store>(env, b, i + 1, [&](const store& st) {
if (!inst.s.isGP()) return false;
if (!st.s.isGP()) return false;
const auto rv = is_adjacent_vptr64(inst.d, st.d, 8, -512, 504);
if (rv != 0) {
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rv < 0) {
v << storepair{inst.s, st.s, inst.d};
} else {
v << storepair{st.s, inst.s, st.d};
}
return 2;
});
}
return false;
if (!is_valid_reg_for_storepair(env, inst.s)) return false;
if (!is_valid_reg_for_storepair(env, st.s)) return false;
Vptr ptr;
bool rebase_ptr;
const auto rv = is_adjacent_vptr64(inst.d, st.d, 8, -512, 504, ptr, rebase_ptr);
if (rv == 0 || (rebase_ptr && post_regalloc))
return false;
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rebase_ptr) {
auto const tmp = v.makeReg();
v << lea{ptr, tmp};
ptr.base = tmp;
ptr.disp = 0;
}
if (rv < 0) {
v << storepair{inst.s, st.s, ptr};
} else {
v << storepair{st.s, inst.s, ptr};
}
return 2;
});
});
}

///////////////////////////////////////////////////////////////////////////////

bool simplify(Env& env, const storel& inst, Vlabel b, size_t i) {
bool simplify(Env& env, const storel& inst, Vlabel b, size_t i, bool post_regalloc) {
// storel{s, d}; storel{s, d} --> storepairl{s0, s1, d}
return if_inst<Vinstr::storel>(env, b, i + 1, [&](const storel& st) {
if (!inst.s.isGP()) return false;
if (!st.s.isGP()) return false;
const auto rv = is_adjacent_vptr64(inst.m, st.m, 4, -256, 252);
if (rv != 0) {
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rv < 0) {
v << storepairl{inst.s, st.s, inst.m};
} else {
v << storepairl{st.s, inst.s, st.m};
}
return 2;
});
}
return false;
if (!is_valid_reg_for_storepair(env, inst.s)) return false;
if (!is_valid_reg_for_storepair(env, st.s)) return false;
Vptr ptr;
bool rebase_ptr;
const auto rv = is_adjacent_vptr64(inst.m, st.m, 4, -256, 252, ptr, rebase_ptr);
if (rv == 0 || (rebase_ptr && post_regalloc))
return false;
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rebase_ptr) {
auto const tmp = v.makeReg();
v << lea{ptr, tmp};
ptr.base = tmp;
ptr.disp = 0;
}
if (rv < 0) {
v << storepairl{inst.s, st.s, ptr};
} else {
v << storepairl{st.s, inst.s, ptr};
}
return 2;
});
});
}

///////////////////////////////////////////////////////////////////////////////

bool simplify(Env& env, const load& inst, Vlabel b, size_t i) {
bool simplify(Env& env, const load& inst, Vlabel b, size_t i, bool post_regalloc) {
// load{d, m}; load{d, m} --> loadpair{s, d0, d1}
return if_inst<Vinstr::load>(env, b, i + 1, [&](const load& ld) {
if (inst.d == ld.d) return false;
if (!inst.d.isGP()) return false;
if (!ld.d.isGP()) return false;
const auto rv = is_adjacent_vptr64(inst.s, ld.s, 8, -512, 504);
if (rv != 0) {
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rv < 0) {
v << loadpair{inst.s, inst.d, ld.d};
} else {
v << loadpair{ld.s, ld.d, inst.d};
}
return 2;
});
}
return false;
Vptr ptr;
bool rebase_ptr;
const auto rv = is_adjacent_vptr64(inst.s, ld.s, 8, -512, 504, ptr, rebase_ptr);
if (rv == 0 || (rebase_ptr && post_regalloc))
return false;
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rebase_ptr) {
auto const tmp = v.makeReg();
v << lea{ptr, tmp};
ptr.base = tmp;
ptr.disp = 0;
}
if (rv < 0) {
v << loadpair{ptr, inst.d, ld.d};
} else {
v << loadpair{ptr, ld.d, inst.d};
}
return 2;
});
});
}

///////////////////////////////////////////////////////////////////////////////

bool simplify(Env& env, const loadl& inst, Vlabel b, size_t i) {
bool simplify(Env& env, const loadl& inst, Vlabel b, size_t i, bool post_regalloc) {
// loadl{d, m}; loadl{d, m} --> loadpairl{s, d0, d1}
bool simplified = if_inst<Vinstr::loadl>(env, b, i + 1, [&](const loadl& ld) {
if (inst.d == ld.d) return false;
if (!inst.d.isGP()) return false;
if (!ld.d.isGP()) return false;
const auto rv = is_adjacent_vptr64(inst.s, ld.s, 4, -256, 252);
Vptr ptr;
bool rebase_ptr;
const auto rv = is_adjacent_vptr64(inst.s, ld.s, 4, -256, 252, ptr, rebase_ptr);
if (rv != 0) {
return simplify_impl(env, b, i, [&] (Vout& v) {
if (rebase_ptr) {
auto const tmp = v.makeReg();
v << lea{ptr, tmp};
ptr.base = tmp;
ptr.disp = 0;
}
if (rv < 0) {
v << loadpairl{inst.s, inst.d, ld.d};
v << loadpairl{ptr, inst.d, ld.d};
} else {
v << loadpairl{ld.s, ld.d, inst.d};
v << loadpairl{ptr, ld.d, inst.d};
}
return 2;
});
Expand Down Expand Up @@ -287,20 +360,36 @@ bool simplify(Env& env, const loadl& inst, Vlabel b, size_t i) {

///////////////////////////////////////////////////////////////////////////////

bool simplify(Env& env, const store& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i, false);
}

bool simplify(Env& env, const storel& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i, false);
}

bool simplify(Env& env, const load& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i, false);
}

bool simplify(Env& env, const loadl& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i, false);
}

bool psimplify(Env& env, const store& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i);
return simplify(env, inst, b, i, true);
}

bool psimplify(Env& env, const storel& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i);
return simplify(env, inst, b, i, true);
}

bool psimplify(Env& env, const load& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i);
return simplify(env, inst, b, i, true);
}

bool psimplify(Env& env, const loadl& inst, Vlabel b, size_t i) {
return simplify(env, inst, b, i);
return simplify(env, inst, b, i, true);
}

///////////////////////////////////////////////////////////////////////////////
Expand Down