AnyPS5/core/relinker/codegen/src/x86/Sha1Lowering.cpp

193 lines
6.8 KiB
C++

#include <codegen/x86/Sha1Lowering.hpp>
#include <array>
namespace Codegen {
namespace {
constexpr std::uint8_t kPrefixPacked = 0x66;
constexpr std::uint8_t kShiftRightDwords = 2;
constexpr std::uint8_t kShiftLeftDwords = 6;
constexpr std::uint8_t kShiftRightBytes = 3;
constexpr std::uint8_t kShiftLeftBytes = 7;
constexpr std::uint8_t kWordBits = 32;
constexpr std::uint8_t kPunpckldq = 0x62;
constexpr std::uint8_t kPunpcklqdq = 0x6C;
constexpr std::uint8_t kMovdqa = 0x6F;
constexpr std::uint8_t kPshufd = 0x70;
constexpr std::uint8_t kShufpd = 0xC6;
constexpr std::uint8_t kPand = 0xDB;
constexpr std::uint8_t kPor = 0xEB;
constexpr std::uint8_t kPxor = 0xEF;
constexpr std::uint8_t kPaddd = 0xFE;
constexpr std::array<std::uint32_t, 4> kRoundConstants = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6};
template<std::size_t TCount>
std::array<std::uint8_t, TCount> _scratch(const Sha1Operands& operands) {
std::array<std::uint8_t, TCount> scratch{};
for (std::uint8_t reg = 0, found = 0; found < TCount; ++reg)
if (reg != operands.Destination && reg != operands.Source) scratch[found++] = reg;
return scratch;
}
void _op(StubBodyBuilder& body, const std::uint8_t opcode, const std::uint8_t dst, const std::uint8_t src) {
body.Sse(kPrefixPacked, {0x0F, opcode}, dst, src);
}
void _lane(StubBodyBuilder& body, const std::uint8_t dst, const std::uint8_t src, const std::uint8_t lane) {
body.SseImm(kPrefixPacked, {0x0F, kPshufd}, dst, src, lane);
}
void _rotate(StubBodyBuilder& body, const std::uint8_t reg, const std::uint8_t tmp, const std::uint8_t count) {
_op(body, kMovdqa, tmp, reg);
body.ShiftDwordImm(kShiftLeftDwords, tmp, count);
body.ShiftDwordImm(kShiftRightDwords, reg, static_cast<std::uint8_t>(kWordBits - count));
_op(body, kPor, reg, tmp);
}
void _function(StubBodyBuilder& body, const std::uint8_t function, const std::uint8_t out, const std::uint8_t b, const std::uint8_t c, const std::uint8_t d, const std::uint8_t tmp) {
switch (function) {
case 0:
_op(body, kMovdqa, out, c);
_op(body, kPxor, out, d);
_op(body, kPand, out, b);
_op(body, kPxor, out, d);
return;
case 2:
_op(body, kMovdqa, out, b);
_op(body, kPand, out, c);
_op(body, kMovdqa, tmp, b);
_op(body, kPor, tmp, c);
_op(body, kPand, tmp, d);
_op(body, kPor, out, tmp);
return;
default:
_op(body, kMovdqa, out, b);
_op(body, kPxor, out, c);
_op(body, kPxor, out, d);
return;
}
}
StubConstant _roundConstant(const std::uint8_t function) {
StubConstant constant{};
for (std::size_t index = 0; index < constant.size(); ++index)
constant[index] = static_cast<std::uint8_t>(kRoundConstants[function] >> ((index % 4) * 8));
return constant;
}
void _emitRounds(StubBodyBuilder& body, const Sha1Operands& operands) {
const auto scratch = _scratch<7>(operands);
std::array<std::uint8_t, 5> state = {scratch[0], scratch[1], scratch[2], scratch[3], scratch[4]};
const auto t = scratch[5];
const auto u = scratch[6];
for (const auto reg : scratch)
body.Spill(reg);
for (std::uint8_t index = 0; index < 4; ++index)
_lane(body, state[index], operands.Destination, static_cast<std::uint8_t>(3 - index));
_op(body, kPxor, state[4], state[4]);
for (std::uint8_t round = 0; round < 4; ++round) {
const auto [a, b, c, d, e] = state;
_function(body, operands.Function, t, b, c, d, u);
_op(body, kPaddd, e, t);
_op(body, kMovdqa, t, a);
_rotate(body, t, u, 5);
_op(body, kPaddd, e, t);
_lane(body, t, operands.Source, static_cast<std::uint8_t>(3 - round));
_op(body, kPaddd, e, t);
body.RipOperand({0x0F, kPaddd}, e, _roundConstant(operands.Function));
_rotate(body, b, u, 30);
state = {e, a, b, c, d};
}
const auto [a, b, c, d, e] = state;
_op(body, kPunpckldq, d, c);
_op(body, kPunpckldq, b, a);
_op(body, kPunpcklqdq, d, b);
_op(body, kMovdqa, operands.Destination, d);
for (auto reg = scratch.rbegin(); reg != scratch.rend(); ++reg)
body.Restore(*reg);
}
void _emitNext(StubBodyBuilder& body, const Sha1Operands& operands) {
const auto scratch = _scratch<2>(operands);
const auto rotated = scratch[0];
const auto tmp = scratch[1];
for (const auto reg : scratch)
body.Spill(reg);
_op(body, kMovdqa, rotated, operands.Destination);
_rotate(body, rotated, tmp, 30);
body.ShiftImm(kShiftRightBytes, rotated, 12);
body.ShiftImm(kShiftLeftBytes, rotated, 12);
_op(body, kMovdqa, operands.Destination, operands.Source);
_op(body, kPaddd, operands.Destination, rotated);
for (auto reg = scratch.rbegin(); reg != scratch.rend(); ++reg)
body.Restore(*reg);
}
void _emitMessage1(StubBodyBuilder& body, const Sha1Operands& operands) {
const auto scratch = _scratch<1>(operands);
const auto words = scratch[0];
body.Spill(words);
_op(body, kMovdqa, words, operands.Source);
body.SseImm(kPrefixPacked, {0x0F, kShufpd}, words, operands.Destination, 1);
_op(body, kPxor, operands.Destination, words);
body.Restore(words);
}
void _emitMessage2(StubBodyBuilder& body, const Sha1Operands& operands) {
const auto dst = operands.Destination;
const auto scratch = _scratch<2>(operands);
const auto words = scratch[0];
const auto tmp = scratch[1];
for (const auto reg : scratch)
body.Spill(reg);
_op(body, kMovdqa, words, operands.Source);
body.ShiftImm(kShiftLeftBytes, words, 4);
_op(body, kPxor, dst, words);
_rotate(body, dst, tmp, 1);
_op(body, kMovdqa, words, dst);
body.ShiftImm(kShiftRightBytes, words, 12);
_rotate(body, words, tmp, 1);
_op(body, kPxor, dst, words);
for (auto reg = scratch.rbegin(); reg != scratch.rend(); ++reg)
body.Restore(*reg);
}
}
void Sha1Lowering::EmitOutOfLine(StubBodyBuilder& body, const Sha1Operands& operands) const {
if (operands.Memory) {
auto loaded = operands;
loaded.Memory.reset();
loaded.Source = loaded.Destination;
loaded.Source = _scratch<1>(loaded)[0];
body.Spill(loaded.Source);
body.Load(loaded.Source, *operands.Memory);
EmitOutOfLine(body, loaded);
body.Restore(loaded.Source);
return;
}
switch (operands.Operation) {
case Sha1Operation::Rnds4:
_emitRounds(body, operands);
return;
case Sha1Operation::Nexte:
_emitNext(body, operands);
return;
case Sha1Operation::Msg1:
_emitMessage1(body, operands);
return;
case Sha1Operation::Msg2:
_emitMessage2(body, operands);
return;
}
}
LoweredBody Sha1Lowering::LowerOutOfLine(const Sha1Operands& operands) const {
StubBodyBuilder body;
EmitOutOfLine(body, operands);
return body.Finish();
}
}