mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 16:00:18 +02:00
Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fe5eb1979 | ||
|
|
f414c92963 | ||
|
|
90c59e37cb | ||
|
|
b7c7789a01 | ||
|
|
f653c5e0c0 | ||
|
|
65fff73959 | ||
|
|
93b7c513d8 | ||
|
|
f1d14c6325 | ||
|
|
8223c6ac36 | ||
|
|
e17677580d | ||
|
|
150bf7b30c | ||
|
|
674efc69c4 | ||
|
|
38049c5281 | ||
|
|
e3627349a1 | ||
|
|
7c207080a4 | ||
|
|
eeee5b53ca | ||
|
|
47619063c2 | ||
|
|
cb7076cbab | ||
|
|
8dde79826e | ||
|
|
4e1d10a46f | ||
|
|
46d019fe02 | ||
|
|
d56f689e15 | ||
|
|
90702b4102 | ||
|
|
0cf105b64a | ||
|
|
5c74d9458c | ||
|
|
1ab79bd72e | ||
|
|
d716e22476 | ||
|
|
bbb8e1ccab | ||
|
|
96f20779c6 | ||
|
|
908313e378 | ||
|
|
12e5c60633 | ||
|
|
11946ffc4c | ||
|
|
f44cd9c545 | ||
|
|
9c5ccb13de | ||
|
|
e9bcfd4784 | ||
|
|
fd1e8d4566 | ||
|
|
68dcce0739 | ||
|
|
3c554cd787 | ||
|
|
9e5f2269d9 | ||
|
|
81fc502c6c | ||
|
|
eda8ca5449 | ||
|
|
35dd8972e2 | ||
|
|
643dd56b74 | ||
|
|
b748eab4ed | ||
|
|
f4eaab6977 | ||
|
|
900c114831 | ||
|
|
7937b7e52d | ||
|
|
b40e707771 | ||
|
|
edde5c8516 | ||
|
|
197facb845 | ||
|
|
ca697d0d5d | ||
|
|
32ddf790d5 | ||
|
|
2cfba8c6d0 | ||
|
|
cf6c0765fa | ||
|
|
ad93f27271 | ||
|
|
92428e5cbc | ||
|
|
02c00a87dd | ||
|
|
6c77bc12c1 | ||
|
|
fbb428c249 | ||
|
|
854a741ea4 | ||
|
|
ed1952a79a | ||
|
|
39e8f5122f | ||
|
|
6ee77eb1a1 | ||
|
|
46fc45b952 | ||
|
|
79d90f3c7f | ||
|
|
eb41cb2261 | ||
|
|
87e8a9b6aa | ||
|
|
69f5aa8e35 | ||
|
|
d654f55c3c | ||
|
|
eb1689e79f | ||
|
|
cf6472df90 | ||
|
|
bc6295a78d | ||
|
|
ed5774b88f | ||
|
|
40a29ca9a7 | ||
|
|
a1e1838b11 | ||
|
|
4731dbab2f | ||
|
|
f2841ccb5e | ||
|
|
81a474fe12 | ||
|
|
5af2477d00 | ||
|
|
4cbba94a29 | ||
|
|
2a57428314 | ||
|
|
f9ac57205b | ||
|
|
3c6246f99a | ||
|
|
072e7bd241 | ||
|
|
4080dca816 | ||
|
|
c22fb80129 | ||
|
|
123fcd4bea | ||
|
|
e0c17672f2 | ||
|
|
53d484c5f4 | ||
|
|
a2ea767b77 | ||
|
|
4bd51a4be0 | ||
|
|
474f2dc267 | ||
|
|
033310234e | ||
|
|
10835c4b62 | ||
|
|
3e741df77b | ||
|
|
31d4b4a201 | ||
|
|
678985cbf5 | ||
|
|
e83e9cb6b3 | ||
|
|
48a51679b8 | ||
|
|
987fc925ee | ||
|
|
b6cbb23781 | ||
|
|
cbb9017c12 | ||
|
|
832d1e8d2c | ||
|
|
34af7f942b | ||
|
|
5f390c16be | ||
|
|
f36ac0498d | ||
|
|
18bdd9b665 | ||
|
|
5fd3852fd3 | ||
|
|
764432c77c | ||
|
|
55c43229ca | ||
|
|
dbc4daaaa4 | ||
|
|
acdbbec78d | ||
|
|
3247e777c0 | ||
|
|
c6e60ff3f5 | ||
|
|
b7df102919 | ||
|
|
f5d450b95c | ||
|
|
972aaf9f5f | ||
|
|
b98d5f30c0 | ||
|
|
d2e2e189d8 | ||
|
|
17d110575c | ||
|
|
a967976420 | ||
|
|
7e1e1ccccc | ||
|
|
502452602e | ||
|
|
f8ff46f3e3 | ||
|
|
8f572deaed | ||
|
|
601dd1d2b5 | ||
|
|
673e826e46 | ||
|
|
f1f81f9de2 | ||
|
|
8513c02ec1 | ||
|
|
282f091e85 | ||
|
|
8647033029 |
No files matched your search
@@ -36,24 +36,33 @@ public:
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
}
|
||||
|
||||
void adr(ARMEmitter::Register rd, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded adr(ARMEmitter::Register rd, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b0001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
if (IsADRRange(Imm)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
void adr(ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded adr(ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::ADR});
|
||||
constexpr uint32_t Op = 0b0001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void adr(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded adr(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
adr(rd, &Label->Backward);
|
||||
return adr(rd, &Label->Backward);
|
||||
} else {
|
||||
adr(rd, &Label->Forward);
|
||||
return adr(rd, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,32 +71,42 @@ public:
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
}
|
||||
|
||||
void adrp(ARMEmitter::Register rd, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded adrp(ARMEmitter::Register rd, const BackwardLabel* Label) {
|
||||
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>() & ~0xFFFLL);
|
||||
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b1001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
if (IsADRPRange(Imm) && IsADRPAligned(Imm)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b1001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
void adrp(ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
|
||||
[[nodiscard]] BranchEncodeSucceeded adrp(ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::ADRP});
|
||||
constexpr uint32_t Op = 0b1001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void adrp(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded adrp(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
adrp(rd, &Label->Backward);
|
||||
return adrp(rd, &Label->Backward);
|
||||
} else {
|
||||
adrp(rd, &Label->Forward);
|
||||
return adrp(rd, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
void LongAddressGen(ARMEmitter::Register rd, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, const BackwardLabel* Label) {
|
||||
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>());
|
||||
if (IsADRRange(Imm)) {
|
||||
// If the range is in ADR range then we can just use ADR.
|
||||
adr(rd, Label);
|
||||
return adr(rd, Label);
|
||||
} else if (IsADRPRange(Imm)) {
|
||||
int64_t ADRPImm = (reinterpret_cast<int64_t>(Label->Location) & ~0xFFFLL) - (GetCursorAddress<int64_t>() & ~0xFFFLL);
|
||||
|
||||
@@ -102,23 +121,28 @@ public:
|
||||
// Now even an add
|
||||
add(ARMEmitter::Size::i64Bit, rd, rd, AlignedOffset);
|
||||
}
|
||||
} else {
|
||||
LOGMAN_MSG_A_FMT("Unscaled offset too large");
|
||||
FEX_UNREACHABLE;
|
||||
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
void LongAddressGen(ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::LONG_ADDRESS_GEN});
|
||||
// Emit a register index and a nop. These will be backpatched.
|
||||
dc32(rd.Idx());
|
||||
nop();
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void LongAddressGen(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded LongAddressGen(ARMEmitter::Register rd, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
LongAddressGen(rd, &Label->Backward);
|
||||
return LongAddressGen(rd, &Label->Backward);
|
||||
} else {
|
||||
LongAddressGen(rd, &Label->Forward);
|
||||
return LongAddressGen(rd, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,12 +886,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr Condition InvertCondition(Condition cond) {
|
||||
// These behave as always, so it makes no sense to allow inverting these.
|
||||
LOGMAN_THROW_A_FMT(cond != Condition::CC_AL && cond != Condition::CC_NV, "Cannot invert CC_AL or CC_NV");
|
||||
return static_cast<Condition>(FEXCore::ToUnderlying(cond) ^ 1);
|
||||
}
|
||||
|
||||
void and_(ARMEmitter::Size s, ARMEmitter::Register rd, ARMEmitter::Register rn, uint32_t n, uint32_t immr, uint32_t imms) {
|
||||
constexpr uint32_t Op = 0b001'0010'00 << 22;
|
||||
DataProcessing_Logical_Imm(Op, s, rd, rn, n, immr, imms);
|
||||
|
||||
@@ -20,23 +20,31 @@ public:
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 0, Cond, Imm);
|
||||
}
|
||||
void b(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded b(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 0, Cond, Imm >> 2);
|
||||
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 0, Cond, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
void b(ARMEmitter::Condition Cond, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded b(ARMEmitter::Condition Cond, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 0, Cond, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void b(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded b(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
b(Cond, &Label->Backward);
|
||||
return b(Cond, &Label->Backward);
|
||||
} else {
|
||||
b(Cond, &Label->Forward);
|
||||
return b(Cond, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,24 +53,32 @@ public:
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 1, Cond, Imm);
|
||||
}
|
||||
void bc(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded bc(ARMEmitter::Condition Cond, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 1, Cond, Imm >> 2);
|
||||
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 1, Cond, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
|
||||
void bc(ARMEmitter::Condition Cond, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded bc(ARMEmitter::Condition Cond, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
|
||||
constexpr uint32_t Op = 0b0101'010 << 25;
|
||||
Branch_Conditional(Op, 0, 1, Cond, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void bc(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded bc(ARMEmitter::Condition Cond, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
bc(Cond, &Label->Backward);
|
||||
return bc(Cond, &Label->Backward);
|
||||
} else {
|
||||
bc(Cond, &Label->Forward);
|
||||
return bc(Cond, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,25 +114,32 @@ public:
|
||||
|
||||
UnconditionalBranch(Op, Imm);
|
||||
}
|
||||
void b(const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded b(const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
constexpr uint32_t Op = 0b0001'01 << 26;
|
||||
if (Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0001'01 << 26;
|
||||
UnconditionalBranch(Op, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
UnconditionalBranch(Op, Imm >> 2);
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
void b(ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded b(ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::B});
|
||||
constexpr uint32_t Op = 0b0001'01 << 26;
|
||||
|
||||
UnconditionalBranch(Op, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void b(BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded b(BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
b(&Label->Backward);
|
||||
return b(&Label->Backward);
|
||||
} else {
|
||||
b(&Label->Forward);
|
||||
return b(&Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,25 +149,33 @@ public:
|
||||
UnconditionalBranch(Op, Imm);
|
||||
}
|
||||
|
||||
void bl(const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded bl(const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
constexpr uint32_t Op = 0b1001'01 << 26;
|
||||
if (Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b1001'01 << 26;
|
||||
UnconditionalBranch(Op, Imm >> 2);
|
||||
|
||||
UnconditionalBranch(Op, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
void bl(ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded bl(ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::B});
|
||||
constexpr uint32_t Op = 0b1001'01 << 26;
|
||||
|
||||
UnconditionalBranch(Op, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void bl(BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded bl(BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
bl(&Label->Backward);
|
||||
return bl(&Label->Backward);
|
||||
} else {
|
||||
bl(&Label->Forward);
|
||||
return bl(&Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,28 +186,35 @@ public:
|
||||
CompareAndBranch(Op, s, rt, Imm);
|
||||
}
|
||||
|
||||
void cbz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded cbz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0100 << 24;
|
||||
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0011'0100 << 24;
|
||||
CompareAndBranch(Op, s, rt, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
CompareAndBranch(Op, s, rt, Imm >> 2);
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
|
||||
void cbz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded cbz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0100 << 24;
|
||||
|
||||
CompareAndBranch(Op, s, rt, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void cbz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded cbz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
cbz(s, rt, &Label->Backward);
|
||||
return cbz(s, rt, &Label->Backward);
|
||||
} else {
|
||||
cbz(s, rt, &Label->Forward);
|
||||
return cbz(s, rt, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,28 +224,35 @@ public:
|
||||
CompareAndBranch(Op, s, rt, Imm);
|
||||
}
|
||||
|
||||
void cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0101 << 24;
|
||||
if (Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0011'0101 << 24;
|
||||
CompareAndBranch(Op, s, rt, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
CompareAndBranch(Op, s, rt, Imm >> 2);
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
|
||||
void cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::BC});
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0101 << 24;
|
||||
|
||||
CompareAndBranch(Op, s, rt, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded cbnz(ARMEmitter::Size s, ARMEmitter::Register rt, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
cbnz(s, rt, &Label->Backward);
|
||||
return cbnz(s, rt, &Label->Backward);
|
||||
} else {
|
||||
cbnz(s, rt, &Label->Forward);
|
||||
return cbnz(s, rt, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,28 +262,35 @@ public:
|
||||
|
||||
TestAndBranch(Op, rt, Bit, Imm);
|
||||
}
|
||||
void tbz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded tbz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0110 << 24;
|
||||
if (Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0011'0110 << 24;
|
||||
TestAndBranch(Op, rt, Bit, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
TestAndBranch(Op, rt, Bit, Imm >> 2);
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
|
||||
void tbz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded tbz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::TEST_BRANCH});
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0110 << 24;
|
||||
|
||||
TestAndBranch(Op, rt, Bit, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void tbz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded tbz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
tbz(rt, Bit, &Label->Backward);
|
||||
return tbz(rt, Bit, &Label->Backward);
|
||||
} else {
|
||||
tbz(rt, Bit, &Label->Forward);
|
||||
return tbz(rt, Bit, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,27 +299,35 @@ public:
|
||||
|
||||
TestAndBranch(Op, rt, Bit, Imm);
|
||||
}
|
||||
void tbnz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded tbnz(ARMEmitter::Register rt, uint32_t Bit, const BackwardLabel* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b0011'0111 << 24;
|
||||
if (Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0)) [[likely]] {
|
||||
constexpr uint32_t Op = 0b0011'0111 << 24;
|
||||
TestAndBranch(Op, rt, Bit, Imm >> 2);
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
TestAndBranch(Op, rt, Bit, Imm >> 2);
|
||||
// Can't encode.
|
||||
return BranchEncodeSucceeded::Failure;
|
||||
}
|
||||
|
||||
void tbnz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded tbnz(ARMEmitter::Register rt, uint32_t Bit, ForwardLabel* Label) {
|
||||
AddLocationToLabel(Label, ForwardLabel::Reference {.Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::InstType::TEST_BRANCH});
|
||||
constexpr uint32_t Op = 0b0011'0111 << 24;
|
||||
|
||||
TestAndBranch(Op, rt, Bit, 0);
|
||||
|
||||
// Forward label doesn't know if it can encode until Bind.
|
||||
return BranchEncodeSucceeded::Success;
|
||||
}
|
||||
|
||||
void tbnz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] BranchEncodeSucceeded tbnz(ARMEmitter::Register rt, uint32_t Bit, BiDirectionalLabel* Label) {
|
||||
if (Label->Backward.Location) {
|
||||
tbnz(rt, Bit, &Label->Backward);
|
||||
return tbnz(rt, Bit, &Label->Backward);
|
||||
} else {
|
||||
tbnz(rt, Bit, &Label->Forward);
|
||||
return tbnz(rt, Bit, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -586,6 +586,15 @@ concept IsXOrWRegister = std::is_same_v<T, XRegister> || std::is_same_v<T, WRegi
|
||||
template<typename T>
|
||||
concept IsQOrDRegister = std::is_same_v<T, QRegister> || std::is_same_v<T, DRegister>;
|
||||
|
||||
template<typename T>
|
||||
concept IsLabel = std::is_same_v<T, ARMEmitter::ForwardLabel> || std::is_same_v<T, ARMEmitter::BackwardLabel> ||
|
||||
std::is_same_v<T, ARMEmitter::BiDirectionalLabel> || std::is_same_v<T, ARMEmitter::ForwardLabel::Reference>;
|
||||
|
||||
enum class BranchEncodeSucceeded {
|
||||
Success,
|
||||
Failure,
|
||||
};
|
||||
|
||||
// Whether or not a given set of vector registers are sequential
|
||||
// in increasing order as far as the register file is concerned (modulo its size)
|
||||
//
|
||||
@@ -638,19 +647,25 @@ public:
|
||||
|
||||
// Bind a backward label to an address.
|
||||
// Address that is bound is the current emitter location.
|
||||
void Bind(BackwardLabel* Label) {
|
||||
[[nodiscard]] bool Bind(BackwardLabel* Label) {
|
||||
LOGMAN_THROW_A_FMT(Label->Location == nullptr, "Trying to bind a label twice");
|
||||
Label->Location = GetCursorAddress<uint8_t*>();
|
||||
|
||||
// Always binds because it is only storing a location.
|
||||
return true;
|
||||
}
|
||||
|
||||
void Bind(const ForwardLabel::Reference* Label) {
|
||||
[[nodiscard]] bool Bind(const ForwardLabel::Reference* Label) {
|
||||
uint8_t* CurrentAddress = GetCursorAddress<uint8_t*>();
|
||||
// Patch up the instructions
|
||||
switch (Label->Type) {
|
||||
case ForwardLabel::InstType::ADR: {
|
||||
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
|
||||
if (!IsADRRange(Imm)) [[unlikely]] {
|
||||
// Can't bind.
|
||||
return false;
|
||||
}
|
||||
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
|
||||
uint32_t Inst = *Instruction & ~InstMask;
|
||||
@@ -662,7 +677,12 @@ public:
|
||||
case ForwardLabel::InstType::ADRP: {
|
||||
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
|
||||
|
||||
if (!(IsADRPRange(Imm) && IsADRPAligned(Imm))) [[unlikely]] {
|
||||
// Can't bind.
|
||||
return false;
|
||||
}
|
||||
|
||||
Imm >>= 12;
|
||||
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
|
||||
@@ -672,11 +692,13 @@ public:
|
||||
*Instruction = Inst;
|
||||
break;
|
||||
}
|
||||
|
||||
case ForwardLabel::InstType::B: {
|
||||
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
if (!(Imm >= -134217728 && Imm <= 134217724 && ((Imm & 0b11) == 0))) [[unlikely]] {
|
||||
// Can't bind.
|
||||
return false;
|
||||
}
|
||||
Imm >>= 2;
|
||||
uint32_t InstMask = 0x3FF'FFFF;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & InstMask;
|
||||
@@ -686,11 +708,13 @@ public:
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ForwardLabel::InstType::TEST_BRANCH: {
|
||||
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
if (!(Imm >= -32768 && Imm <= 32764 && ((Imm & 0b11) == 0))) [[unlikely]] {
|
||||
// Can't bind.
|
||||
return false;
|
||||
}
|
||||
Imm >>= 2;
|
||||
uint32_t InstMask = 0x3FFF;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & InstMask;
|
||||
@@ -704,7 +728,10 @@ public:
|
||||
case ForwardLabel::InstType::RELATIVE_LOAD: {
|
||||
uint32_t* Instruction = reinterpret_cast<uint32_t*>(Label->Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0), "Unscaled offset too large");
|
||||
if (!(Imm >= -1048576 && Imm <= 1048575 && ((Imm & 0b11) == 0))) [[unlikely]] {
|
||||
// Can't bind.
|
||||
return false;
|
||||
}
|
||||
Imm >>= 2;
|
||||
uint32_t InstMask = 0x7'FFFF;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & InstMask;
|
||||
@@ -753,27 +780,41 @@ public:
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unexpected inst type in label fixup");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bind a forward label to a location.
|
||||
// This walks all the instructions in the label's vector.
|
||||
// Then backpatching all instructions that have used the label.
|
||||
void Bind(ForwardLabel* Label) {
|
||||
[[nodiscard]] bool Bind(ForwardLabel* Label) {
|
||||
bool Bound = true;
|
||||
if (Label->FirstInst.Location) {
|
||||
Bind(&Label->FirstInst);
|
||||
Bound &= Bind(&Label->FirstInst);
|
||||
}
|
||||
for (auto& Inst : Label->Insts) {
|
||||
Bind(&Inst);
|
||||
Bound &= Bind(&Inst);
|
||||
}
|
||||
|
||||
return Bound;
|
||||
}
|
||||
|
||||
// Bind a bidirectional location to a location.
|
||||
// Binds both forwards and backwards depending on how the label was used.
|
||||
void Bind(BiDirectionalLabel* Label) {
|
||||
[[nodiscard]] bool Bind(BiDirectionalLabel* Label) {
|
||||
bool Bound = true;
|
||||
if (!Label->Backward.Location) {
|
||||
Bind(&Label->Backward);
|
||||
Bound &= Bind(&Label->Backward);
|
||||
}
|
||||
Bind(&Label->Forward);
|
||||
Bound &= Bind(&Label->Forward);
|
||||
|
||||
return Bound;
|
||||
}
|
||||
|
||||
static constexpr Condition InvertCondition(Condition cond) {
|
||||
// These behave as always, so it makes no sense to allow inverting these.
|
||||
LOGMAN_THROW_A_FMT(cond != Condition::CC_AL && cond != Condition::CC_NV, "Cannot invert CC_AL or CC_NV");
|
||||
return static_cast<Condition>(FEXCore::ToUnderlying(cond) ^ 1);
|
||||
}
|
||||
|
||||
#include <CodeEmitter/VixlUtils.inl>
|
||||
|
||||
@@ -66,6 +66,7 @@ set (SRCS
|
||||
Interface/IR/Passes/RedundantFlagCalculationElimination.cpp
|
||||
Interface/IR/Passes/RegisterAllocationPass.cpp
|
||||
Interface/IR/Passes/x87StackOptimizationPass.cpp
|
||||
Utils/LongJump.cpp
|
||||
Utils/Telemetry.cpp
|
||||
Utils/Threads.cpp
|
||||
Utils/Profiler.cpp
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include <FEXCore/Utils/TypeDefines.h>
|
||||
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace FEXCore {
|
||||
// Buffered JIT symbol tracking.
|
||||
struct JITSymbolBuffer {
|
||||
// Maximum buffer size to ensure we are a page in size.
|
||||
constexpr static size_t BUFFER_SIZE = 4096 - (8 * 2);
|
||||
constexpr static size_t BUFFER_SIZE = FEXCore::Utils::FEX_PAGE_SIZE - (8 * 2);
|
||||
// Maximum distance until the end of the buffer to do a write.
|
||||
constexpr static size_t NEEDS_WRITE_DISTANCE = BUFFER_SIZE - 64;
|
||||
// Maximum time threshhold to wait before a buffer write occurs.
|
||||
@@ -27,7 +28,7 @@ struct JITSymbolBuffer {
|
||||
size_t Offset {};
|
||||
char Buffer[BUFFER_SIZE] {};
|
||||
};
|
||||
static_assert(sizeof(JITSymbolBuffer) == 4096, "Ensure this is one page in size");
|
||||
static_assert(sizeof(JITSymbolBuffer) == FEXCore::Utils::FEX_PAGE_SIZE, "Ensure this is one page in size");
|
||||
|
||||
class JITSymbols final {
|
||||
public:
|
||||
|
||||
@@ -504,47 +504,6 @@ struct FEX_PACKED X80SoftFloat {
|
||||
return std::bit_cast<float>(Result);
|
||||
}
|
||||
|
||||
bool IsSignalingNaN() const {
|
||||
return (Exponent == 0x7FFF) && (Significand & 0x8000000000000000ULL) && !(Significand & 0x4000000000000000ULL) && // Bit 62 clear (signaling)
|
||||
(Significand & 0x3FFFFFFFFFFFFFFFULL);
|
||||
}
|
||||
|
||||
bool IsQuietNaN() const {
|
||||
return (Exponent == 0x7FFF) && (Significand & 0x8000000000000000ULL) && (Significand & 0x4000000000000000ULL); // Bit 62 set (quiet)
|
||||
}
|
||||
|
||||
// Helper to detect if this is any NaN
|
||||
bool IsNaN() const {
|
||||
return IsSignalingNaN() || IsQuietNaN();
|
||||
}
|
||||
|
||||
// X87 value to F64 while preserving signaling nan property
|
||||
double ToF64_PreserveNan(softfloat_state* state) const {
|
||||
if (IsSignalingNaN()) {
|
||||
// we keep it as a signaling nan in ieee754 in 64bits
|
||||
uint64_t sign_bit = Sign ? 0x8000000000000000ULL : 0;
|
||||
uint64_t exp_bits = 0x7FF0000000000000ULL;
|
||||
uint64_t x87_frac = Significand & 0x3FFFFFFFFFFFFFFFULL;
|
||||
uint64_t ieee_frac = (x87_frac >> 11) & 0x0007FFFFFFFFFFFFULL;
|
||||
|
||||
if (ieee_frac == 0) {
|
||||
ieee_frac = 1;
|
||||
}
|
||||
ieee_frac &= ~0x0008000000000000ULL;
|
||||
|
||||
uint64_t result_bits = sign_bit | exp_bits | ieee_frac;
|
||||
return std::bit_cast<double>(result_bits);
|
||||
} else if (IsQuietNaN()) {
|
||||
const float64_t Result = extF80_to_f64(state, *this);
|
||||
uint64_t result_bits = std::bit_cast<uint64_t>(Result);
|
||||
result_bits |= 0x0008000000000000ULL;
|
||||
return std::bit_cast<double>(result_bits);
|
||||
} else {
|
||||
const float64_t Result = extF80_to_f64(state, *this);
|
||||
return std::bit_cast<double>(Result);
|
||||
}
|
||||
}
|
||||
|
||||
double ToF64(softfloat_state* state) const {
|
||||
const float64_t Result = extF80_to_f64(state, *this);
|
||||
return std::bit_cast<double>(Result);
|
||||
@@ -625,39 +584,6 @@ struct FEX_PACKED X80SoftFloat {
|
||||
*this = f64_to_extF80(state, std::bit_cast<float64_t>(rhs));
|
||||
}
|
||||
|
||||
// Create X80SoftFloat from double while preserving NaN signaling properties
|
||||
static X80SoftFloat FromF64_PreserveNaN(softfloat_state* state, double value) {
|
||||
uint64_t bits = std::bit_cast<uint64_t>(value);
|
||||
|
||||
// Check if it's a nan
|
||||
if ((bits & 0x7FF0000000000000ULL) == 0x7FF0000000000000ULL && (bits & 0x000FFFFFFFFFFFFFULL) != 0) {
|
||||
|
||||
X80SoftFloat result;
|
||||
result.Sign = (bits >> 63) & 1;
|
||||
result.Exponent = 0x7FFF;
|
||||
|
||||
bool is_signaling = !(bits & 0x0008000000000000ULL);
|
||||
uint64_t ieee_payload = bits & 0x0007FFFFFFFFFFFFULL;
|
||||
|
||||
// set bit 63 required for x87
|
||||
result.Significand = 0x8000000000000000ULL;
|
||||
|
||||
if (is_signaling) { // clear bit 62 for signaling nan
|
||||
result.Significand &= ~0x4000000000000000ULL;
|
||||
} else { // clear bit 62 for quiet nan
|
||||
result.Significand |= 0x4000000000000000ULL;
|
||||
}
|
||||
|
||||
// ieee754 51-bit payload -> x87 62-bit payload
|
||||
result.Significand |= (ieee_payload << 11) & 0x3FFFFFFFFFFFFFFFULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// For non-NaN values, use standard conversion
|
||||
return X80SoftFloat(state, value);
|
||||
}
|
||||
|
||||
X80SoftFloat(softfloat_state* state, BIGFLOAT rhs) {
|
||||
#if BIGFLOATSIZE == 16
|
||||
*this = f128_to_extF80(state, std::bit_cast<float128_t>(rhs));
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#ifdef _M_X86_64
|
||||
#include <xmmintrin.h>
|
||||
#include <immintrin.h>
|
||||
#else
|
||||
#include <cstdint>
|
||||
#endif
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
@@ -161,6 +161,44 @@
|
||||
"Desc": [
|
||||
"Allows the user to pass additional arguments to the application"
|
||||
]
|
||||
},
|
||||
"DisableL2Cache": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Disables FEXCore's JIT L2 cache lookup. Saving memory.",
|
||||
"Can potentially introduce more stutters."
|
||||
]
|
||||
},
|
||||
"DynamicL1Cache": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Switches FEXCore's JIT L1 cache to be dynamically sized. Saving memory.",
|
||||
"Can potentially introduce more stutters."
|
||||
]
|
||||
},
|
||||
"DynamicL1CacheIncreaseCountHeuristic": {
|
||||
"Type": "uint64",
|
||||
"Default": "250",
|
||||
"Desc": [
|
||||
"Threshold of lookups per second that the L1 dynamic cache should increase its size.",
|
||||
"Lower numbers means more aggressive scaling upward to the maximum size.",
|
||||
"Higher numbers means more conservative scaling, using less memory.",
|
||||
"Can potentially introduce stutters, more likely the higher the number.",
|
||||
"Don't have this number smaller than the decrease count!"
|
||||
]
|
||||
},
|
||||
"DynamicL1CacheDecreaseCountHeuristic": {
|
||||
"Type": "uint64",
|
||||
"Default": "50",
|
||||
"Desc": [
|
||||
"Threshold of lookups per second that the L1 dynamic cache should decrease its size.",
|
||||
"The higher the number, the more aggressively it reduces the L1 cache size.",
|
||||
"Lower numbers means more conservative memory savings.",
|
||||
"Can potentially introduce more stutters, more likely the higher the number.",
|
||||
"Don't have this number larger than the increase count!"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Debug": {
|
||||
@@ -330,6 +368,13 @@
|
||||
"Enables FEX's low-overhead sampling profile statistics.",
|
||||
"Requires a supported version of Mangohud to see the results"
|
||||
]
|
||||
},
|
||||
"EnableGpuvisProfiling": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Enables profiling when FEX was built with the gpuvis profiler backend."
|
||||
]
|
||||
}
|
||||
},
|
||||
"Hacks": {
|
||||
@@ -389,7 +434,7 @@
|
||||
"Default": "true",
|
||||
"Desc": [
|
||||
"Use volatile metadata in PE files to inform TSO instructions when available.",
|
||||
"When metadata is unavailable falls back to the currently enabled TSO options."
|
||||
"When metadata is unavailable falls back to the currently enabled TSO options."
|
||||
]
|
||||
},
|
||||
"X87ReducedPrecision": {
|
||||
@@ -399,31 +444,6 @@
|
||||
"Emulates X87 floating point using 64-bit precision. This reduces emulation accuracy and may result in rendering bugs."
|
||||
]
|
||||
},
|
||||
"X87StrictReducedPrecision": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Enables stricter X87 floating point behavior when X87ReducedPrecision is enabled.",
|
||||
"Adds additional checks and implementations like NaN propagation for better compatibility."
|
||||
]
|
||||
},
|
||||
"ABILocalFlags": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"When enabled enables an optimization around flags.",
|
||||
"Assumes flags are not used across cals.",
|
||||
"Hand-written assembly can violate this assumption."
|
||||
]
|
||||
},
|
||||
"ParanoidTSO": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Makes TSO operations even more strict.",
|
||||
"Forces vector loadstores to also become atomic."
|
||||
]
|
||||
},
|
||||
"StallProcess": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
namespace FEXCore {
|
||||
class SignalDelegator;
|
||||
class ThunkHandler;
|
||||
struct LookupCacheWriteLockToken;
|
||||
|
||||
namespace Core {
|
||||
struct DebugData;
|
||||
@@ -61,7 +62,7 @@ struct CustomIRResult {
|
||||
, Data(Data) {}
|
||||
};
|
||||
|
||||
using BlockDelinkerFunc = void (*)(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record);
|
||||
using BlockDelinkerFunc = void (*)(FEXCore::Context::ExitFunctionLinkData* Record);
|
||||
constexpr uint32_t TSC_SCALE_MAXIMUM = 1'000'000'000; ///< 1Ghz
|
||||
|
||||
class CodeCache : public AbstractCodeCache {
|
||||
@@ -154,10 +155,10 @@ public:
|
||||
return CodeCache;
|
||||
}
|
||||
|
||||
void OnCodeBufferAllocated(CPU::CodeBuffer&) override;
|
||||
void OnCodeBufferAllocated(const std::shared_ptr<CPU::CodeBuffer> &) override;
|
||||
void ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, bool NewCodeBuffer = true) override;
|
||||
void InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* Thread, InvalidatedEntryAccumulator& Accumulator, uint64_t Start,
|
||||
uint64_t Length) override;
|
||||
void InvalidateCodeBuffersCodeRange(uint64_t Start, uint64_t Length) override;
|
||||
void InvalidateThreadCachedCodeRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) override;
|
||||
FEXCore::ForkableSharedMutex& GetCodeInvalidationMutex() override {
|
||||
return CodeInvalidationMutex;
|
||||
}
|
||||
@@ -197,7 +198,6 @@ public:
|
||||
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
|
||||
FEX_CONFIG_OPT(VectorTSOEnabled, VECTORTSOENABLED);
|
||||
FEX_CONFIG_OPT(MemcpySetTSOEnabled, MEMCPYSETTSOENABLED);
|
||||
FEX_CONFIG_OPT(ABILocalFlags, ABILOCALFLAGS);
|
||||
FEX_CONFIG_OPT(SMCChecks, SMCCHECKS);
|
||||
FEX_CONFIG_OPT(MaxInstPerBlock, MAXINST);
|
||||
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
|
||||
@@ -205,9 +205,7 @@ public:
|
||||
FEX_CONFIG_OPT(LibraryJITNaming, LIBRARYJITNAMING);
|
||||
FEX_CONFIG_OPT(BlockJITNaming, BLOCKJITNAMING);
|
||||
FEX_CONFIG_OPT(GDBSymbols, GDBSYMBOLS);
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
|
||||
FEX_CONFIG_OPT(x87StrictReducedPrecision, X87STRICTREDUCEDPRECISION);
|
||||
FEX_CONFIG_OPT(DisableTelemetry, DISABLETELEMETRY);
|
||||
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
|
||||
FEX_CONFIG_OPT(SmallTSCScale, SMALLTSCSCALE);
|
||||
@@ -233,8 +231,6 @@ public:
|
||||
|
||||
ContextImpl(const FEXCore::HostFeatures& Features);
|
||||
|
||||
static bool ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP);
|
||||
|
||||
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame* Frame, uint64_t GuestRIP);
|
||||
|
||||
// This is used as a replacement for the SMC writes in the mono callsite backpatcher that avoids atomic operations
|
||||
@@ -269,9 +265,9 @@ public:
|
||||
|
||||
FEXCore::JITSymbols Symbols;
|
||||
|
||||
FEXCore::Utils::PooledAllocatorVirtual OpDispatcherAllocator;
|
||||
FEXCore::Utils::PooledAllocatorVirtual FrontendAllocator;
|
||||
FEXCore::Utils::PooledAllocatorVirtual CPUBackendAllocator;
|
||||
FEXCore::Utils::PooledAllocatorVirtual OpDispatcherAllocator {"FEXMem_OpDispatcher"};
|
||||
FEXCore::Utils::PooledAllocatorVirtual FrontendAllocator {"FEXMem_Frontend"};
|
||||
FEXCore::Utils::PooledAllocatorVirtual CPUBackendAllocator {"FEXMem_CPUBackend"};
|
||||
|
||||
// If Atomic-based TSO emulation is enabled or not.
|
||||
bool IsAtomicTSOEnabled() const {
|
||||
@@ -312,10 +308,6 @@ protected:
|
||||
AtomicTSOEmulationEnabled = false;
|
||||
VectorAtomicTSOEmulationEnabled = false;
|
||||
MemcpyAtomicTSOEmulationEnabled = false;
|
||||
} else if (Config.ParanoidTSO) {
|
||||
AtomicTSOEmulationEnabled = true;
|
||||
VectorAtomicTSOEmulationEnabled = true;
|
||||
MemcpyAtomicTSOEmulationEnabled = true;
|
||||
} else {
|
||||
AtomicTSOEmulationEnabled = Config.TSOEnabled;
|
||||
VectorAtomicTSOEmulationEnabled = Config.TSOEnabled && Config.VectorTSOEnabled;
|
||||
@@ -323,13 +315,6 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateX87PrecisionConfig() {
|
||||
// If strict reduced precision is enabled, automatically enable reduced precision
|
||||
if (Config.x87StrictReducedPrecision() && !Config.x87ReducedPrecision()) {
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_X87REDUCEDPRECISION, "1");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Initializes the JIT compilers for the thread
|
||||
@@ -361,5 +346,8 @@ private:
|
||||
|
||||
bool MonoDetected = false;
|
||||
std::atomic<uint64_t> MonoBackpatcherBlock;
|
||||
|
||||
std::mutex CodeBufferListLock;
|
||||
fextl::vector<std::weak_ptr<CPU::CodeBuffer>> CodeBufferList;
|
||||
};
|
||||
} // namespace FEXCore::Context
|
||||
@@ -360,9 +360,7 @@ namespace CPU {
|
||||
LogMan::Msg::EFmt("Failed to mprotect last page of code buffer.");
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, "FEXMemJIT");
|
||||
#endif
|
||||
FEXCore::Allocator::VirtualName("FEXMemJIT", reinterpret_cast<void*>(Ptr), Size);
|
||||
|
||||
LookupCache = fextl::make_unique<GuestToHostMap>();
|
||||
}
|
||||
@@ -402,7 +400,7 @@ namespace CPU {
|
||||
Latest = Buffer;
|
||||
LatestOffset = 0;
|
||||
|
||||
OnCodeBufferAllocated(*Buffer);
|
||||
OnCodeBufferAllocated(Buffer);
|
||||
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace CPU {
|
||||
// Protects writes to the latest CodeBuffer and changes to LatestOffset
|
||||
FEXCore::ForkableUniqueMutex CodeBufferWriteMutex;
|
||||
|
||||
virtual void OnCodeBufferAllocated(CodeBuffer&) {};
|
||||
virtual void OnCodeBufferAllocated(const std::shared_ptr<CodeBuffer>&) {};
|
||||
|
||||
private:
|
||||
fextl::shared_ptr<CodeBuffer> Latest;
|
||||
|
||||
@@ -57,13 +57,20 @@ $end_info$
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <fcntl.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <shared_mutex>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string_view>
|
||||
#include <sys/stat.h>
|
||||
#include <type_traits>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <xxhash.h>
|
||||
|
||||
@@ -93,8 +100,6 @@ ContextImpl::ContextImpl(const FEXCore::HostFeatures& Features)
|
||||
|
||||
// Track atomic TSO emulation configuration.
|
||||
UpdateAtomicTSOEmulationConfig();
|
||||
// Ensure X87 precision constraints are respected.
|
||||
UpdateX87PrecisionConfig();
|
||||
}
|
||||
|
||||
struct GetFrameBlockInfoResult {
|
||||
@@ -371,7 +376,9 @@ void ContextImpl::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread)
|
||||
Thread->FrontendDecoder = fextl::make_unique<FEXCore::Frontend::Decoder>(Thread);
|
||||
Thread->PassManager = fextl::make_unique<FEXCore::IR::PassManager>();
|
||||
|
||||
Thread->CurrentFrame->Pointers.Common.L1Pointer = Thread->LookupCache->GetL1Pointer();
|
||||
Thread->CurrentFrame->State.L1Pointer = Thread->LookupCache->GetL1Pointer();
|
||||
Thread->CurrentFrame->State.L1Mask = Thread->LookupCache->GetScaledL1PointerMask();
|
||||
|
||||
Thread->CurrentFrame->Pointers.Common.L2Pointer = Thread->LookupCache->GetPagePointer();
|
||||
|
||||
Dispatcher->InitThreadPointers(Thread);
|
||||
@@ -393,6 +400,7 @@ ContextImpl::CreateThread(uint64_t InitialRIP, uint64_t StackPointer, const FEXC
|
||||
FEXCore::Core::InternalThreadState* Thread = new FEXCore::Core::InternalThreadState {
|
||||
.CTX = this,
|
||||
};
|
||||
FEXCore::Allocator::VirtualName("FEXMem_ThreadState", Thread, sizeof(*Thread));
|
||||
|
||||
Thread->CurrentFrame->State.gregs[X86State::REG_RSP] = StackPointer;
|
||||
Thread->CurrentFrame->State.rip = InitialRIP;
|
||||
@@ -451,9 +459,14 @@ void ContextImpl::LockBeforeFork(FEXCore::Core::InternalThreadState* Thread) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void ContextImpl::OnCodeBufferAllocated(CPU::CodeBuffer& Buffer) {
|
||||
void ContextImpl::OnCodeBufferAllocated(const fextl::shared_ptr<CPU::CodeBuffer>& Buffer) {
|
||||
if (Config.GlobalJITNaming()) {
|
||||
Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
|
||||
Symbols.RegisterJITSpace(Buffer->Ptr, Buffer->Size);
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lk{CodeBufferListLock};
|
||||
CodeBufferList.emplace_back(Buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,7 +478,8 @@ void ContextImpl::ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, boo
|
||||
Thread->CPUBackend->ClearCache();
|
||||
} else {
|
||||
// Clear L1+L2 cache of this thread, and clear L3 cache across any threads using it
|
||||
Thread->LookupCache->ClearCache();
|
||||
auto lk = Thread->LookupCache->AcquireWriteLock();
|
||||
Thread->LookupCache->ClearCache(lk);
|
||||
}
|
||||
Allocator::VirtualDontNeed(Thread->CallRetStackBase, FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE);
|
||||
}
|
||||
@@ -637,10 +651,10 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue
|
||||
LogMan::Msg::EFmt("Invalid or Unknown instruction: {} 0x{:x}", TableInfo->Name ?: "UND", Block.Entry - GuestRIP);
|
||||
}
|
||||
|
||||
if (Block.BlockStatus == Frontend::Decoder::DecodedBlockStatus::NOEXEC_INST) {
|
||||
Thread->OpDispatcher->NoExecOp(DecodedInfo);
|
||||
} else {
|
||||
if (Block.BlockStatus == Frontend::Decoder::DecodedBlockStatus::INVALID_INST) {
|
||||
Thread->OpDispatcher->InvalidOp(DecodedInfo);
|
||||
} else {
|
||||
Thread->OpDispatcher->NoExecOp(DecodedInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,7 +737,7 @@ ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalT
|
||||
// but this would increase lock contention. Redundant frontend runs aren't
|
||||
// as expensive and are easily reverted.
|
||||
if (MaxInst != 1) {
|
||||
if (auto Block = Thread->LookupCache->FindBlock(GuestRIP)) {
|
||||
if (auto Block = Thread->LookupCache->FindBlock(Thread, GuestRIP)) {
|
||||
Thread->OpDispatcher->DelayedDisownBuffer();
|
||||
return {.CompiledCode = {.BlockBegin = reinterpret_cast<uint8_t*>(Block), .EntryPoints = {{GuestRIP, reinterpret_cast<uint8_t*>(Block)}}},
|
||||
.DebugData = nullptr,
|
||||
@@ -764,10 +778,13 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_
|
||||
|
||||
// Is the code in the cache?
|
||||
// The backends only check L1 and L2, not L3
|
||||
if (auto HostCode = Thread->LookupCache->FindBlock(GuestRIP)) {
|
||||
if (auto HostCode = Thread->LookupCache->FindBlock(Thread, GuestRIP)) {
|
||||
return HostCode;
|
||||
}
|
||||
|
||||
// Accumulate a JIT count now, as even if another thread raced us, it should count as a compile.
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Thread, AccumulatedJITCount, 1);
|
||||
|
||||
auto [CompiledCode, DebugData, StartAddr, Length, NeedsAddGuestCodeRanges] = CompileCode(Thread, GuestRIP, MaxInst);
|
||||
auto CodePtr = CompiledCode.EntryPoints[GuestRIP];
|
||||
if (CodePtr == nullptr) {
|
||||
@@ -821,20 +838,25 @@ uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame* Frame, uint64_
|
||||
Thread->CPUBackend->ClearRelocations();
|
||||
}
|
||||
|
||||
fextl::vector<uint64_t> CodePages;
|
||||
|
||||
if (NeedsAddGuestCodeRanges) {
|
||||
// Track in the guest to host map all entrypoints for all pages the compiled block touches, if any page didn't previously
|
||||
// contain code, inform the frontend so it can setup SMC detection.
|
||||
auto BlockInfo = Thread->FrontendDecoder->GetDecodedBlockInfo();
|
||||
CodePages.reserve(BlockInfo->CodePages.size());
|
||||
CodePages.insert(CodePages.end(), BlockInfo->CodePages.begin(), BlockInfo->CodePages.end());
|
||||
for (auto CodePage : BlockInfo->CodePages) {
|
||||
if (Thread->LookupCache->AddBlockExecutableRange(BlockInfo->EntryPoints, CodePage, FEXCore::Utils::FEX_PAGE_SIZE)) {
|
||||
if (Thread->LookupCache->AddBlockExecutableRange(Thread, BlockInfo->EntryPoints, CodePage, FEXCore::Utils::FEX_PAGE_SIZE)) {
|
||||
SyscallHandler->MarkGuestExecutableRange(Thread, CodePage, FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert to lookup cache
|
||||
|
||||
for (auto [GuestAddr, HostAddr] : CompiledCode.EntryPoints) {
|
||||
Thread->LookupCache->AddBlockMapping(GuestAddr, HostAddr);
|
||||
Thread->LookupCache->AddBlockMapping(Thread, GuestAddr, CodePages, HostAddr);
|
||||
}
|
||||
|
||||
return (uintptr_t)CodePtr;
|
||||
@@ -861,49 +883,37 @@ uintptr_t ContextImpl::CompileSingleStep(FEXCore::Core::CpuStateFrame* Frame, ui
|
||||
return (uintptr_t)CodePtr;
|
||||
}
|
||||
|
||||
static void InvalidateGuestThreadCodeRange(FEXCore::Core::InternalThreadState* Thread, InvalidatedEntryAccumulator& Accumulator,
|
||||
uint64_t Start, uint64_t Length) {
|
||||
// Ensures now-modified mappings aren't cached as being in their previous non-executable state.
|
||||
void ContextImpl::InvalidateCodeBuffersCodeRange(uint64_t Start, uint64_t Length) {
|
||||
FEXCORE_PROFILE_SCOPED("InvalidateCodeBuffersCodeRange");
|
||||
|
||||
LogMan::Throw::AFmt(CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
std::scoped_lock lk {CodeBufferListLock};
|
||||
auto it = CodeBufferList.begin();
|
||||
while (it != CodeBufferList.end()) {
|
||||
if (auto Strong = it->lock(); Strong) {
|
||||
Strong->LookupCache->InvalidateRange(Start, Length);
|
||||
it++;
|
||||
} else {
|
||||
it = CodeBufferList.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContextImpl::InvalidateThreadCachedCodeRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) {
|
||||
LogMan::Throw::AFmt(CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
|
||||
// Ensures now-modified mappings aren't cached as being in their previous non-executable state.
|
||||
// Accessing FrontendDecoder is safe as the thread's code invalidation mutex must be locked here.
|
||||
Thread->FrontendDecoder->ResetExecutableRangeCache();
|
||||
|
||||
auto lk = Thread->LookupCache->AcquireLock();
|
||||
auto& CodePages = Thread->LookupCache->Shared->CodePages;
|
||||
if (Thread->LookupCache->InvalidateCacheRange(Start, Length)) {
|
||||
FEXCORE_PROFILE_SCOPED("InvalidateCallRet");
|
||||
|
||||
auto lower = CodePages.lower_bound(Start >> 12);
|
||||
auto upper = CodePages.upper_bound((Start + Length - 1) >> 12);
|
||||
|
||||
for (auto it = lower; it != upper; it++) {
|
||||
Accumulator.emplace_back(std::move(it->second));
|
||||
}
|
||||
|
||||
bool InvalidatedAnyEntries = false;
|
||||
for (const auto& PageEntries : Accumulator) {
|
||||
for (const auto& Entry : PageEntries) {
|
||||
if (ContextImpl::ThreadRemoveCodeEntry(Thread, Entry)) {
|
||||
InvalidatedAnyEntries = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (InvalidatedAnyEntries) {
|
||||
// This may cause access violations in the thread on Windows as zeroing is not atomic, this is handled by the frontend
|
||||
Allocator::VirtualDontNeed(Thread->CallRetStackBase, FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void ContextImpl::InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* Thread, InvalidatedEntryAccumulator& Accumulator,
|
||||
uint64_t Start, uint64_t Length) {
|
||||
InvalidateGuestThreadCodeRange(Thread, Accumulator, Start, Length);
|
||||
}
|
||||
|
||||
bool ContextImpl::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestRIP) {
|
||||
LogMan::Throw::AFmt(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to "
|
||||
"be unique_locked here");
|
||||
|
||||
return Thread->LookupCache->Erase(Thread->CurrentFrame, GuestRIP);
|
||||
}
|
||||
|
||||
void ContextImpl::ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame* Frame, uint64_t GuestRIP) {
|
||||
static_cast<ContextImpl*>(Frame->Thread->CTX)->SyscallHandler->InvalidateGuestCodeRange(Frame->Thread, GuestRIP, 1);
|
||||
}
|
||||
@@ -971,7 +981,7 @@ void ContextImpl::AddThunkTrampolineIRHandler(uintptr_t Entrypoint, uintptr_t Gu
|
||||
void ContextImpl::AddForceTSOInformation(const IntervalList<uint64_t>& ValidRanges, fextl::set<uint64_t>&& Instructions) {
|
||||
LogMan::Throw::AFmt(CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
ForceTSOValidRanges.Insert(ValidRanges);
|
||||
ForceTSOInstructions.merge(Instructions);
|
||||
ForceTSOInstructions.merge(std::move(Instructions));
|
||||
}
|
||||
|
||||
void ContextImpl::RemoveForceTSOInformation(uint64_t Address, uint64_t Size) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "Common/SoftFloat.h"
|
||||
#include "Common/VectorRegType.h"
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/CPUBackend.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
@@ -26,9 +26,7 @@
|
||||
#endif
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
|
||||
@@ -38,12 +36,14 @@ static void SleepThread(FEXCore::Context::ContextImpl* CTX, FEXCore::Core::CpuSt
|
||||
CTX->SyscallHandler->SleepThread(CTX, Frame);
|
||||
}
|
||||
|
||||
constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096 * 4;
|
||||
constexpr size_t MAX_DISPATCHER_CODE_SIZE = FEXCore::Utils::FEX_PAGE_SIZE * 4;
|
||||
|
||||
Dispatcher::Dispatcher(FEXCore::Context::ContextImpl* ctx)
|
||||
: Arm64Emitter(ctx, FEXCore::Allocator::VirtualAlloc(MAX_DISPATCHER_CODE_SIZE, true), MAX_DISPATCHER_CODE_SIZE)
|
||||
, CTX {ctx} {
|
||||
EmitDispatcher();
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(GetBufferBase()), MAX_DISPATCHER_CODE_SIZE);
|
||||
}
|
||||
|
||||
Dispatcher::~Dispatcher() {
|
||||
@@ -93,7 +93,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
FillStaticRegs();
|
||||
ldr(RipReg, STATE_PTR(CpuStateFrame, State.rip));
|
||||
cbnz(ARMEmitter::Size::i32Bit, ENTRY_FILL_SRA_SINGLE_INST_REG, &CompileSingleStep);
|
||||
(void)cbnz(ARMEmitter::Size::i32Bit, ENTRY_FILL_SRA_SINGLE_INST_REG, &CompileSingleStep);
|
||||
|
||||
ARMEmitter::BiDirectionalLabel LoopTop {};
|
||||
|
||||
@@ -142,7 +142,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
// We want to ensure that we are 16 byte aligned at the top of this loop
|
||||
Align16B();
|
||||
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
AbsoluteLoopTopAddress = GetCursorAddress<uint64_t>();
|
||||
|
||||
// Load in our RIP
|
||||
@@ -169,66 +169,73 @@ void Dispatcher::EmitDispatcher() {
|
||||
ldr(TMP2, STATE_PTR(CpuStateFrame, Pointers.Common.ExitFunctionEC));
|
||||
br(TMP2);
|
||||
|
||||
Bind(&l_NotECCode);
|
||||
(void)Bind(&l_NotECCode);
|
||||
#endif
|
||||
|
||||
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
|
||||
cbnz(ARMEmitter::Size::i32Bit, TMP1, &CompileSingleStep);
|
||||
|
||||
// This is the block cache lookup routine
|
||||
// It matches what is going on it LookupCache.h::FindBlock
|
||||
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.L2Pointer));
|
||||
|
||||
// Mask the address by the virtual address size so we can check for aliases
|
||||
uint64_t VirtualMemorySize = CTX->Config.VirtualMemSize;
|
||||
if (std::popcount(VirtualMemorySize) == 1) {
|
||||
and_(ARMEmitter::Size::i64Bit, TMP4, RipReg.R(), VirtualMemorySize - 1);
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP4, VirtualMemorySize);
|
||||
and_(ARMEmitter::Size::i64Bit, TMP4, RipReg.R(), TMP4);
|
||||
}
|
||||
(void)cbnz(ARMEmitter::Size::i32Bit, TMP1, &CompileSingleStep);
|
||||
|
||||
ARMEmitter::ForwardLabel NoBlock;
|
||||
|
||||
{
|
||||
// Offset the address and add to our page pointer
|
||||
lsr(ARMEmitter::Size::i64Bit, TMP2, TMP4, 12);
|
||||
if (DisableL2Cache()) {
|
||||
(void)b(&NoBlock);
|
||||
} else {
|
||||
// This is the block cache lookup routine
|
||||
// It matches what is going on it LookupCache.h::FindBlock
|
||||
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.L2Pointer));
|
||||
|
||||
// Load the pointer from the offset
|
||||
ldr(TMP1, TMP1, TMP2, ARMEmitter::ExtendedType::LSL_64, 3);
|
||||
// Mask the address by the virtual address size so we can check for aliases
|
||||
uint64_t VirtualMemorySize = CTX->Config.VirtualMemSize;
|
||||
if (std::popcount(VirtualMemorySize) == 1) {
|
||||
and_(ARMEmitter::Size::i64Bit, TMP4, RipReg.R(), VirtualMemorySize - 1);
|
||||
} else {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP4, VirtualMemorySize);
|
||||
and_(ARMEmitter::Size::i64Bit, TMP4, RipReg.R(), TMP4);
|
||||
}
|
||||
|
||||
// If page pointer is zero then we have no block
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &NoBlock);
|
||||
|
||||
// Steal the page offset
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, TMP4, 0x0FFF);
|
||||
|
||||
// Shift the offset by the size of the block cache entry
|
||||
add(TMP1, TMP1, TMP2, ARMEmitter::ShiftType::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
|
||||
|
||||
// The the full LookupCacheEntry with a single LDP.
|
||||
// Check the guest address first to ensure it maps to the address we are currently at.
|
||||
// This fixes aliasing problems
|
||||
ldp<ARMEmitter::IndexType::OFFSET>(TMP4, TMP2, TMP1, 0);
|
||||
|
||||
// If the guest address doesn't match, Compile the block.
|
||||
sub(TMP2, TMP2, RipReg);
|
||||
cbnz(ARMEmitter::Size::i64Bit, TMP2, &NoBlock);
|
||||
|
||||
// Check the host address to see if it matches, else compile the block.
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP4, &NoBlock);
|
||||
|
||||
// If we've made it here then we have a real compiled block
|
||||
{
|
||||
// update L1 cache
|
||||
ldr(TMP1, STATE_PTR(CpuStateFrame, Pointers.Common.L1Pointer));
|
||||
// Offset the address and add to our page pointer
|
||||
lsr(ARMEmitter::Size::i64Bit, TMP2, TMP4, 12);
|
||||
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, RipReg.R(), LookupCache::L1_ENTRIES_MASK);
|
||||
add(TMP1, TMP1, TMP2, ARMEmitter::ShiftType::LSL, 4);
|
||||
stp<ARMEmitter::IndexType::OFFSET>(TMP4, RipReg, TMP1);
|
||||
// Load the pointer from the offset
|
||||
ldr(TMP1, TMP1, TMP2, ARMEmitter::ExtendedType::LSL_64, 3);
|
||||
|
||||
// Jump to the block
|
||||
br(TMP4);
|
||||
// If page pointer is zero then we have no block
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &NoBlock);
|
||||
|
||||
// Steal the page offset
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, TMP4, 0x0FFF);
|
||||
|
||||
// Shift the offset by the size of the block cache entry
|
||||
add(TMP1, TMP1, TMP2, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(sizeof(LookupCache::LookupCacheEntry)));
|
||||
|
||||
// The the full LookupCacheEntry with a single LDP.
|
||||
// Check the guest address first to ensure it maps to the address we are currently at.
|
||||
// This fixes aliasing problems
|
||||
ldp<ARMEmitter::IndexType::OFFSET>(TMP4, TMP2, TMP1, 0);
|
||||
|
||||
// If the guest address doesn't match, Compile the block.
|
||||
sub(TMP2, TMP2, RipReg);
|
||||
(void)cbnz(ARMEmitter::Size::i64Bit, TMP2, &NoBlock);
|
||||
|
||||
// Check the host address to see if it matches, else compile the block.
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP4, &NoBlock);
|
||||
|
||||
// If we've made it here then we have a real compiled block
|
||||
{
|
||||
// update L1 cache
|
||||
ldp<ARMEmitter::IndexType::OFFSET>(TMP1, TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, State.L1Pointer));
|
||||
|
||||
// Calculate (tmp1 + ((ripreg & L1_ENTRIES_MASK) << 4)) for the address
|
||||
// L1Mask is pre-shifted.
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, TMP2, RipReg.R(), ARMEmitter::ShiftType::LSL, FEXCore::ilog2(sizeof(LookupCache::LookupCacheEntry)));
|
||||
add(TMP1, TMP1, TMP2);
|
||||
|
||||
stp<ARMEmitter::IndexType::OFFSET>(TMP4, RipReg, TMP1);
|
||||
|
||||
// Jump to the block
|
||||
br(TMP4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +311,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
|
||||
// Need to create the block
|
||||
{
|
||||
Bind(&NoBlock);
|
||||
(void)Bind(&NoBlock);
|
||||
|
||||
EmitSignalGuardedRegion([&]() {
|
||||
SpillStaticRegs(TMP1);
|
||||
@@ -338,7 +345,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
}
|
||||
|
||||
{
|
||||
Bind(&CompileSingleStep);
|
||||
(void)Bind(&CompileSingleStep);
|
||||
|
||||
EmitSignalGuardedRegion([&]() {
|
||||
SpillStaticRegs(TMP1);
|
||||
@@ -500,7 +507,7 @@ void Dispatcher::EmitDispatcher() {
|
||||
stp<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::zr, ARMEmitter::XReg::zr, REG_CALLRET_SP, -0x10);
|
||||
|
||||
// Now go back to the regular dispatcher loop
|
||||
b(&LoopTop);
|
||||
(void)b(&LoopTop);
|
||||
}
|
||||
|
||||
auto EmitLongALUOpHandler = [&](auto R, auto Offset) {
|
||||
@@ -569,14 +576,15 @@ void Dispatcher::EmitDispatcher() {
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&l_CTX);
|
||||
(void)Bind(&l_CTX);
|
||||
dc64(reinterpret_cast<uintptr_t>(CTX));
|
||||
Bind(&l_Sleep);
|
||||
(void)Bind(&l_Sleep);
|
||||
dc64(reinterpret_cast<uint64_t>(SleepThread));
|
||||
Bind(&l_CompileBlock);
|
||||
(void)Bind(&l_CompileBlock);
|
||||
FEXCore::Utils::MemberFunctionToPointerCast PMFCompileBlock(&FEXCore::Context::ContextImpl::CompileBlock);
|
||||
dc64(PMFCompileBlock.GetConvertedPointer());
|
||||
Bind(&l_CompileSingleStep);
|
||||
(void)Bind(&l_CompileSingleStep);
|
||||
|
||||
FEXCore::Utils::MemberFunctionToPointerCast PMFCompileSingleStep(&FEXCore::Context::ContextImpl::CompileSingleStep);
|
||||
dc64(PMFCompileSingleStep.GetConvertedPointer());
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Interface/Core/ArchHelpers/Arm64Emitter.h"
|
||||
#include "Interface/Core/Interpreter/InterpreterOps.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
|
||||
#include <array>
|
||||
@@ -92,6 +93,8 @@ private:
|
||||
|
||||
void EmitDispatcher();
|
||||
uint64_t GenerateABICall(FallbackABI ABI);
|
||||
|
||||
FEX_CONFIG_OPT(DisableL2Cache, DISABLEL2CACHE);
|
||||
};
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -1047,8 +1047,11 @@ Decoder::DecodedBlockStatus Decoder::DecodeInstruction(uint64_t PC) {
|
||||
// Put an invalid instruction in the stream so the core can raise SIGILL if hit
|
||||
// Error while decoding instruction. We don't know the table or instruction size
|
||||
DecodeInst->TableInfo = nullptr;
|
||||
auto Result = ErrorDuringDecoding ? DecodedBlockStatus::INVALID_INST :
|
||||
DecodeInst->InstSize ? DecodedBlockStatus::PARTIAL_DECODE_INST :
|
||||
DecodedBlockStatus::NOEXEC_INST;
|
||||
DecodeInst->InstSize = 0;
|
||||
return ErrorDuringDecoding ? DecodedBlockStatus::INVALID_INST : DecodedBlockStatus::NOEXEC_INST;
|
||||
return Result;
|
||||
} else if (!DecodeInst->TableInfo || (DecodeInst->TableInfo->Type == TYPE_INST && !DecodeInst->TableInfo->OpcodeDispatcher.OpDispatch)) {
|
||||
// If there wasn't an error during decoding but we have no dispatcher for the instruction then claim invalid instruction.
|
||||
return DecodedBlockStatus::INVALID_INST;
|
||||
@@ -1450,7 +1453,10 @@ void Decoder::DecodeInstructionsAtEntry(FEXCore::Core::InternalThreadState* Thre
|
||||
EraseBlock = true;
|
||||
} else {
|
||||
LogMan::Msg::EFmt("{} instruction in entry block: {:X}",
|
||||
BlockIt->BlockStatus == DecodedBlockStatus::INVALID_INST ? "Invalid" : "NoExec", OpAddress);
|
||||
BlockIt->BlockStatus == DecodedBlockStatus::INVALID_INST ? "Invalid" :
|
||||
BlockIt->BlockStatus == DecodedBlockStatus::NOEXEC_INST ? "NoExec" :
|
||||
"PartialDecode",
|
||||
OpAddress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
SUCCESS,
|
||||
INVALID_INST,
|
||||
NOEXEC_INST,
|
||||
PARTIAL_DECODE_INST,
|
||||
};
|
||||
|
||||
// New Frontend decoding
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
#pragma once
|
||||
#include "Common/SoftFloat.h"
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/Interpreter/Fallbacks/FallbackOpHandler.h"
|
||||
#include "Interface/IR/IR.h"
|
||||
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/SHMStats.h>
|
||||
#include <FEXCore/Config/Config.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static softfloat_state SoftFloatStateFromFCW(uint16_t FCW, bool Force80BitPrecision = false) {
|
||||
@@ -79,12 +77,6 @@ struct OpHandlers<IR::OP_F80CVTTO> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static VectorRegType handle8(uint16_t FCW, double src, FEXCore::Core::CpuStateFrame* Frame) {
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
|
||||
ScopedSoftFloatState State {FCW, Frame};
|
||||
auto Context = static_cast<Context::ContextImpl*>(Frame->Thread->CTX);
|
||||
auto ReducedPrecisionMode = Context->Config.x87ReducedPrecision;
|
||||
auto StrictReducedPrecisionMode = Context->Config.x87StrictReducedPrecision;
|
||||
if (!ReducedPrecisionMode || StrictReducedPrecisionMode) {
|
||||
return X80SoftFloat::FromF64_PreserveNaN(&State.State, src);
|
||||
}
|
||||
return X80SoftFloat(&State.State, src);
|
||||
}
|
||||
};
|
||||
@@ -123,12 +115,6 @@ struct OpHandlers<IR::OP_F80CVT> {
|
||||
FEXCORE_PRESERVE_ALL_ATTR static double handle8(uint16_t FCW, VectorRegType src, FEXCore::Core::CpuStateFrame* Frame) {
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Frame->Thread, AccumulatedFloatFallbackCount, 1);
|
||||
ScopedSoftFloatState State {FCW, Frame};
|
||||
auto Context = static_cast<Context::ContextImpl*>(Frame->Thread->CTX);
|
||||
auto ReducedPrecisionMode = Context->Config.x87ReducedPrecision;
|
||||
auto StrictReducedPrecisionMode = Context->Config.x87StrictReducedPrecision;
|
||||
if (!ReducedPrecisionMode || StrictReducedPrecisionMode) {
|
||||
return X80SoftFloat(src).ToF64_PreserveNan(&State.State);
|
||||
}
|
||||
return X80SoftFloat(src).ToF64(&State.State);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -588,7 +588,7 @@ DEF_OP(ShiftFlags) {
|
||||
and_(ARMEmitter::Size::i32Bit, TMP1, Src2, OpSize == IR::OpSize::i64Bit ? 0x3f : 0x1f);
|
||||
|
||||
ARMEmitter::ForwardLabel Done;
|
||||
cbz(EmitSize, TMP1, &Done);
|
||||
(void)cbz(EmitSize, TMP1, &Done);
|
||||
{
|
||||
// PF/SF/ZF/OF
|
||||
if (OpSize >= IR::OpSize::i32Bit) {
|
||||
@@ -652,7 +652,7 @@ DEF_OP(ShiftFlags) {
|
||||
msr(ARMEmitter::SystemRegister::NZCV, TMP2);
|
||||
}
|
||||
}
|
||||
Bind(&Done);
|
||||
(void)Bind(&Done);
|
||||
|
||||
// TODO: Make RA less dumb so this can't happen (e.g. with late-kill).
|
||||
if (PFOutput != PFTemp) {
|
||||
@@ -669,7 +669,7 @@ DEF_OP(RotateFlags) {
|
||||
|
||||
// If shift=0, flags are unaffected. Wrap the whole implementation in a cbz.
|
||||
ARMEmitter::ForwardLabel Done;
|
||||
cbz(EmitSize, Shift, &Done);
|
||||
(void)cbz(EmitSize, Shift, &Done);
|
||||
{
|
||||
// Extract the last bit shifted in to CF
|
||||
const auto BitSize = IR::OpSizeToSize(Op->Size) * 8;
|
||||
@@ -701,7 +701,7 @@ DEF_OP(RotateFlags) {
|
||||
msr(ARMEmitter::SystemRegister::NZCV, TMP3);
|
||||
}
|
||||
}
|
||||
Bind(&Done);
|
||||
(void)Bind(&Done);
|
||||
}
|
||||
|
||||
DEF_OP(Extr) {
|
||||
@@ -767,14 +767,14 @@ DEF_OP(PDep) {
|
||||
// Now, they're copied, so we can start setting Dest (even if it overlaps with
|
||||
// one of them). Handle early exit case
|
||||
mov(EmitSize, Dest, 0);
|
||||
cbz(EmitSize, OrigMask, &Done);
|
||||
(void)cbz(EmitSize, OrigMask, &Done);
|
||||
|
||||
// Setup for first iteration
|
||||
neg(EmitSize, T0, Mask);
|
||||
and_(EmitSize, T0, T0, Mask);
|
||||
|
||||
// Main loop
|
||||
Bind(&NextBit);
|
||||
(void)Bind(&NextBit);
|
||||
sbfx(EmitSize, T1, Input, 0, 1);
|
||||
eor(EmitSize, Mask, Mask, T0);
|
||||
and_(EmitSize, T0, T1, T0);
|
||||
@@ -782,10 +782,10 @@ DEF_OP(PDep) {
|
||||
orr(EmitSize, Dest, Dest, T0);
|
||||
lsr(EmitSize, Input, Input, 1);
|
||||
and_(EmitSize, T0, Mask, T1);
|
||||
cbnz(EmitSize, T0, &NextBit);
|
||||
(void)cbnz(EmitSize, T0, &NextBit);
|
||||
|
||||
// All done with nothing to do.
|
||||
Bind(&Done);
|
||||
(void)Bind(&Done);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,27 +821,27 @@ DEF_OP(PExt) {
|
||||
ARMEmitter::BackwardLabel NextBit;
|
||||
ARMEmitter::ForwardLabel Done;
|
||||
|
||||
cbz(EmitSize, Mask, &EarlyExit);
|
||||
(void)cbz(EmitSize, Mask, &EarlyExit);
|
||||
mov(EmitSize, MaskReg, Mask);
|
||||
mov(EmitSize, ValueReg, Input);
|
||||
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
|
||||
|
||||
// Main loop
|
||||
Bind(&NextBit);
|
||||
cbz(EmitSize, MaskReg, &Done);
|
||||
(void)Bind(&NextBit);
|
||||
(void)cbz(EmitSize, MaskReg, &Done);
|
||||
clz(EmitSize, BitReg, MaskReg);
|
||||
lslv(EmitSize, ValueReg, ValueReg, BitReg);
|
||||
lslv(EmitSize, MaskReg, MaskReg, BitReg);
|
||||
extr(EmitSize, Dest, Dest, ValueReg, OpSizeBitsM1);
|
||||
bfc(EmitSize, MaskReg, OpSizeBitsM1, 1);
|
||||
b(&NextBit);
|
||||
(void)b(&NextBit);
|
||||
|
||||
// Early exit
|
||||
Bind(&EarlyExit);
|
||||
(void)Bind(&EarlyExit);
|
||||
mov(EmitSize, Dest, ARMEmitter::Reg::zr);
|
||||
|
||||
// All done with nothing to do.
|
||||
Bind(&Done);
|
||||
(void)Bind(&Done);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -909,7 +909,7 @@ DEF_OP(Div) {
|
||||
eor(EmitSize, TMP1, TMP1, Upper);
|
||||
|
||||
// If the sign bit matches then the result is zero
|
||||
cbz(EmitSize, TMP1, &Only64Bit);
|
||||
(void)cbz(EmitSize, TMP1, &Only64Bit);
|
||||
|
||||
// Long divide
|
||||
{
|
||||
@@ -928,17 +928,17 @@ DEF_OP(Div) {
|
||||
mov(EmitSize, Remainder, TMP2);
|
||||
|
||||
// Skip 64-bit path
|
||||
b(&LongDIVRet);
|
||||
(void)b(&LongDIVRet);
|
||||
}
|
||||
|
||||
Bind(&Only64Bit);
|
||||
(void)Bind(&Only64Bit);
|
||||
// 64-Bit only
|
||||
{
|
||||
sdiv(EmitSize, Quotient, Lower, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
|
||||
}
|
||||
|
||||
Bind(&LongDIVRet);
|
||||
(void)Bind(&LongDIVRet);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown DIV Size: {}", OpSize); break;
|
||||
@@ -992,7 +992,7 @@ DEF_OP(UDiv) {
|
||||
|
||||
// Check the upper bits for zero
|
||||
// If the upper bits are zero then we can do a 64-bit divide
|
||||
cbz(EmitSize, Upper, &Only64Bit);
|
||||
(void)cbz(EmitSize, Upper, &Only64Bit);
|
||||
|
||||
// Long divide
|
||||
{
|
||||
@@ -1011,17 +1011,17 @@ DEF_OP(UDiv) {
|
||||
mov(EmitSize, Remainder, TMP2);
|
||||
|
||||
// Skip 64-bit path
|
||||
b(&LongDIVRet);
|
||||
(void)b(&LongDIVRet);
|
||||
}
|
||||
|
||||
Bind(&Only64Bit);
|
||||
(void)Bind(&Only64Bit);
|
||||
// 64-Bit only
|
||||
{
|
||||
udiv(EmitSize, Quotient, Lower, Divisor);
|
||||
msub(EmitSize, Remainder, Quotient, Divisor, Lower);
|
||||
}
|
||||
|
||||
Bind(&LongDIVRet);
|
||||
(void)Bind(&LongDIVRet);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unknown LUDIV Size: {}", OpSize); break;
|
||||
|
||||
@@ -63,7 +63,7 @@ void Arm64JITCore::PlaceNamedSymbolLiteral(NamedSymbolLiteralPair& Lit) {
|
||||
auto CurrentCursor = GetCursorAddress<uint8_t*>();
|
||||
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - CodeData.BlockBegin;
|
||||
|
||||
Bind(&Lit.Loc);
|
||||
BindOrRestart(&Lit.Loc);
|
||||
dc64(Lit.Lit);
|
||||
Relocations.emplace_back(Lit.MoveABI);
|
||||
}
|
||||
|
||||
@@ -62,27 +62,27 @@ DEF_OP(CASPair) {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
ARMEmitter::ForwardLabel LoopNotExpected;
|
||||
ARMEmitter::ForwardLabel LoopExpected;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
|
||||
// This instruction sequence must be synced with HandleCASPAL_Armv8.
|
||||
ldaxp(EmitSize, TMP2, TMP3, MemSrc);
|
||||
cmp(EmitSize, TMP2, Expected0);
|
||||
ccmp(EmitSize, TMP3, Expected1, ARMEmitter::StatusFlags::None, ARMEmitter::Condition::CC_EQ);
|
||||
b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
|
||||
(void)b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
|
||||
stlxp(EmitSize, TMP2, Desired0, Desired1, MemSrc);
|
||||
cbnz(EmitSize, TMP2, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP2, &LoopTop);
|
||||
mov(EmitSize, Dst0, Expected0);
|
||||
mov(EmitSize, Dst1, Expected1);
|
||||
|
||||
b(&LoopExpected);
|
||||
(void)b(&LoopExpected);
|
||||
|
||||
Bind(&LoopNotExpected);
|
||||
(void)Bind(&LoopNotExpected);
|
||||
mov(EmitSize, Dst0, TMP2.R());
|
||||
mov(EmitSize, Dst1, TMP3.R());
|
||||
// exclusive monitor needs to be cleared here
|
||||
// Might have hit the case where ldaxr was hit but stlxr wasn't
|
||||
clrex();
|
||||
Bind(&LoopExpected);
|
||||
(void)Bind(&LoopExpected);
|
||||
|
||||
// Restore
|
||||
msr(ARMEmitter::SystemRegister::NZCV, TMP1);
|
||||
@@ -114,7 +114,7 @@ DEF_OP(CAS) {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
ARMEmitter::ForwardLabel LoopNotExpected;
|
||||
ARMEmitter::ForwardLabel LoopExpected;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
if (IROp->Size == IR::OpSize::i8Bit) {
|
||||
cmp(EmitSize, TMP2, Expected, ARMEmitter::ExtendedType::UXTB, 0);
|
||||
@@ -123,18 +123,18 @@ DEF_OP(CAS) {
|
||||
} else {
|
||||
cmp(EmitSize, TMP2, Expected);
|
||||
}
|
||||
b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
|
||||
(void)b(ARMEmitter::Condition::CC_NE, &LoopNotExpected);
|
||||
stlxr(SubEmitSize, TMP3, Desired, MemSrc);
|
||||
cbnz(EmitSize, TMP3, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP3, &LoopTop);
|
||||
mov(EmitSize, Dst, Expected);
|
||||
b(&LoopExpected);
|
||||
(void)b(&LoopExpected);
|
||||
|
||||
Bind(&LoopNotExpected);
|
||||
(void)Bind(&LoopNotExpected);
|
||||
mov(EmitSize, Dst, TMP2.R());
|
||||
// exclusive monitor needs to be cleared here
|
||||
// Might have hit the case where ldaxr was hit but stlxr wasn't
|
||||
clrex();
|
||||
Bind(&LoopExpected);
|
||||
(void)Bind(&LoopExpected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,11 +150,11 @@ DEF_OP(AtomicXor) {
|
||||
steorl(SubEmitSize, Src, MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
eor(EmitSize, TMP2, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP2, TMP2, MemSrc);
|
||||
cbnz(EmitSize, TMP2, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP2, &LoopTop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,10 +179,10 @@ DEF_OP(AtomicSwap) {
|
||||
ldswpal(SubEmitSize, Src, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
stlxr(SubEmitSize, TMP4, Src, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
ubfm(EmitSize, GetReg(Node), TMP2, 0, IR::OpSizeAsBits(OpSize) - 1);
|
||||
}
|
||||
}
|
||||
@@ -199,11 +199,11 @@ DEF_OP(AtomicFetchAdd) {
|
||||
ldaddal(SubEmitSize, Src, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
add(EmitSize, TMP3, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -221,11 +221,11 @@ DEF_OP(AtomicFetchSub) {
|
||||
ldaddal(SubEmitSize, TMP2, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
sub(EmitSize, TMP3, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -243,11 +243,11 @@ DEF_OP(AtomicFetchAnd) {
|
||||
ldclral(SubEmitSize, TMP2, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
and_(EmitSize, TMP3, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -264,11 +264,11 @@ DEF_OP(AtomicFetchCLR) {
|
||||
ldclral(SubEmitSize, Src, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
bic(EmitSize, TMP3, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -285,11 +285,11 @@ DEF_OP(AtomicFetchOr) {
|
||||
ldsetal(SubEmitSize, Src, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
orr(EmitSize, TMP3, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -306,11 +306,11 @@ DEF_OP(AtomicFetchXor) {
|
||||
ldeoral(SubEmitSize, Src, GetReg(Node), MemSrc);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
eor(EmitSize, TMP3, TMP2, Src);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -326,20 +326,20 @@ DEF_OP(AtomicFetchNeg) {
|
||||
// Use a CAS loop to avoid needing to emulate unaligned LLSC atomics
|
||||
ldr(SubEmitSize, TMP2, MemSrc);
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
mov(EmitSize, TMP4, TMP2);
|
||||
neg(EmitSize, TMP3, TMP2);
|
||||
casal(SubEmitSize, TMP2, TMP3, MemSrc);
|
||||
sub(EmitSize, TMP3, TMP2, TMP4);
|
||||
cbnz(EmitSize, TMP3, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP3, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(SubEmitSize, TMP2, MemSrc);
|
||||
neg(EmitSize, TMP3, TMP2);
|
||||
stlxr(SubEmitSize, TMP4, TMP3, MemSrc);
|
||||
cbnz(EmitSize, TMP4, &LoopTop);
|
||||
(void)cbnz(EmitSize, TMP4, &LoopTop);
|
||||
mov(EmitSize, GetReg(Node), TMP2.R());
|
||||
}
|
||||
}
|
||||
@@ -359,11 +359,11 @@ DEF_OP(TelemetrySetValue) {
|
||||
stsetl(ARMEmitter::SubRegSize::i64Bit, TMP1, TMP2);
|
||||
} else {
|
||||
ARMEmitter::BackwardLabel LoopTop;
|
||||
Bind(&LoopTop);
|
||||
(void)Bind(&LoopTop);
|
||||
ldaxr(ARMEmitter::SubRegSize::i64Bit, TMP3, TMP2);
|
||||
orr(ARMEmitter::Size::i32Bit, TMP3, TMP3, Src);
|
||||
stlxr(ARMEmitter::SubRegSize::i64Bit, TMP3, TMP3, TMP2);
|
||||
cbnz(ARMEmitter::Size::i32Bit, TMP3, &LoopTop);
|
||||
(void)cbnz(ARMEmitter::Size::i32Bit, TMP3, &LoopTop);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ DEF_OP(ExitFunction) {
|
||||
if (!Op->CallReturnBlock.IsInvalid()) {
|
||||
auto CallReturnAddressReg = GetReg(Op->CallReturnAddress).X();
|
||||
PendingCallReturnTargetLabel = &CallReturnTargets.try_emplace(Op->CallReturnBlock.ID()).first->second;
|
||||
adr(TMP1, &l_CallReturn);
|
||||
(void)adr(TMP1, &l_CallReturn);
|
||||
stp<ARMEmitter::IndexType::PRE>(CallReturnAddressReg, TMP1, REG_CALLRET_SP, -0x10);
|
||||
} else {
|
||||
stp<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::zr, ARMEmitter::XReg::zr, REG_CALLRET_SP, -0x10);
|
||||
@@ -149,16 +149,16 @@ DEF_OP(ExitFunction) {
|
||||
} else if (Op->Hint == IR::BranchHint::CheckTF) {
|
||||
ARMEmitter::ForwardLabel TFUnset;
|
||||
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
|
||||
cbz(ARMEmitter::Size::i32Bit, TMP1, &TFUnset);
|
||||
(void)cbz(ARMEmitter::Size::i32Bit, TMP1, &TFUnset);
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, NewRIP);
|
||||
str(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip));
|
||||
ldr(TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.DispatcherLoopTop));
|
||||
blr(TMP2);
|
||||
Bind(&TFUnset);
|
||||
(void)Bind(&TFUnset);
|
||||
}
|
||||
|
||||
EmitLinkedBranch(NewRIP, Op->Hint == IR::BranchHint::Call);
|
||||
Bind(&l_CallReturn);
|
||||
(void)Bind(&l_CallReturn);
|
||||
#ifdef _M_ARM_64EC
|
||||
}
|
||||
#endif
|
||||
@@ -170,40 +170,38 @@ DEF_OP(ExitFunction) {
|
||||
// First try to pop from the call-ret stack, otherwise follow the normal path (but ending in a ret)
|
||||
ldp<ARMEmitter::IndexType::POST>(TMP1, TMP2, REG_CALLRET_SP, 0x10);
|
||||
sub(TMP1, TMP1, RipReg.X());
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
|
||||
}
|
||||
|
||||
// L1 Cache
|
||||
ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.L1Pointer));
|
||||
ldp<ARMEmitter::IndexType::OFFSET>(TMP1, TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, State.L1Pointer));
|
||||
|
||||
// Calculate (tmp1 + ((ripreg & L1_ENTRIES_MASK) << 4)) for the address
|
||||
// arithmetic. ubfiz+add is marginally faster on Firestorm than
|
||||
// and+add(shift). Same performance on Cortex.
|
||||
static_assert(LookupCache::L1_ENTRIES_MASK == ((1u << 20) - 1));
|
||||
ubfiz(ARMEmitter::Size::i64Bit, TMP4, RipReg, 4, 20);
|
||||
add(TMP1, TMP1, TMP4);
|
||||
// L1Mask is pre-shifted.
|
||||
and_(ARMEmitter::Size::i64Bit, TMP2, TMP2, RipReg, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(sizeof(LookupCache::LookupCacheEntry)));
|
||||
add(TMP1, TMP1, TMP2);
|
||||
|
||||
ldp<ARMEmitter::IndexType::OFFSET>(TMP2, TMP1, TMP1, 0);
|
||||
|
||||
// Note: sub+cbnz used over cmp+br to preserve flags.
|
||||
sub(TMP1, TMP1, RipReg.X());
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &SkipFullLookup);
|
||||
ldr(TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.DispatcherLoopTop));
|
||||
str(RipReg.X(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip));
|
||||
|
||||
Bind(&SkipFullLookup);
|
||||
(void)Bind(&SkipFullLookup);
|
||||
if (Op->Hint == IR::BranchHint::Call) {
|
||||
ARMEmitter::ForwardLabel l_CallReturn;
|
||||
if (!Op->CallReturnBlock.IsInvalid()) {
|
||||
auto CallReturnAddressReg = GetReg(Op->CallReturnAddress).X();
|
||||
PendingCallReturnTargetLabel = &CallReturnTargets.try_emplace(Op->CallReturnBlock.ID()).first->second;
|
||||
adr(TMP1, &l_CallReturn);
|
||||
(void)adr(TMP1, &l_CallReturn);
|
||||
stp<ARMEmitter::IndexType::PRE>(CallReturnAddressReg, TMP1, REG_CALLRET_SP, -0x10);
|
||||
} else {
|
||||
stp<ARMEmitter::IndexType::PRE>(ARMEmitter::XReg::zr, ARMEmitter::XReg::zr, REG_CALLRET_SP, -0x10);
|
||||
}
|
||||
blr(TMP2);
|
||||
Bind(&l_CallReturn);
|
||||
(void)Bind(&l_CallReturn);
|
||||
} else if (Op->Hint == IR::BranchHint::Return) {
|
||||
ret(TMP2);
|
||||
} else {
|
||||
@@ -224,7 +222,7 @@ DEF_OP(CondJump) {
|
||||
auto TrueTargetLabel = JumpTarget(Op->TrueBlock);
|
||||
|
||||
if (Op->FromNZCV) {
|
||||
b(MapCC(Op->Cond), TrueTargetLabel);
|
||||
b_OrRestart(MapCC(Op->Cond), TrueTargetLabel);
|
||||
} else {
|
||||
uint64_t Const;
|
||||
const bool isConst = IsInlineConstant(Op->Cmp2, &Const);
|
||||
@@ -237,16 +235,16 @@ DEF_OP(CondJump) {
|
||||
|
||||
if (Op->Cond == IR::CondClass::EQ) {
|
||||
LOGMAN_THROW_A_FMT(Const == 0, "CondJump: Expected 0 source");
|
||||
cbz(Size, Reg, TrueTargetLabel);
|
||||
cbz_OrRestart(Size, Reg, TrueTargetLabel);
|
||||
} else if (Op->Cond == IR::CondClass::NEQ) {
|
||||
LOGMAN_THROW_A_FMT(Const == 0, "CondJump: Expected 0 source");
|
||||
cbnz(Size, Reg, TrueTargetLabel);
|
||||
cbnz_OrRestart(Size, Reg, TrueTargetLabel);
|
||||
} else if (Op->Cond == IR::CondClass::TSTZ) {
|
||||
LOGMAN_THROW_A_FMT(Const < 64, "CondJump: Expected valid bit source");
|
||||
tbz(Reg, Const, TrueTargetLabel);
|
||||
tbz_OrRestart(Reg, Const, TrueTargetLabel);
|
||||
} else if (Op->Cond == IR::CondClass::TSTNZ) {
|
||||
LOGMAN_THROW_A_FMT(Const < 64, "CondJump: Expected valid bit source");
|
||||
tbnz(Reg, Const, TrueTargetLabel);
|
||||
tbnz_OrRestart(Reg, Const, TrueTargetLabel);
|
||||
} else {
|
||||
LOGMAN_THROW_A_FMT(false, "CondJump expected simple condition");
|
||||
}
|
||||
@@ -262,16 +260,10 @@ DEF_OP(Syscall) {
|
||||
// X1: ThreadState
|
||||
// X2: Pointer to SyscallArguments
|
||||
|
||||
FEXCore::IR::SyscallFlags Flags = Op->Flags;
|
||||
PushDynamicRegs(TMP1);
|
||||
|
||||
uint32_t GPRSpillMask = ~0U;
|
||||
uint32_t FPRSpillMask = ~0U;
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) == FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) {
|
||||
// Need to spill all caller saved registers still
|
||||
GPRSpillMask = CALLER_GPR_MASK;
|
||||
FPRSpillMask = CALLER_FPR_MASK;
|
||||
}
|
||||
|
||||
SpillStaticRegs(TMP1, true, GPRSpillMask, FPRSpillMask);
|
||||
|
||||
@@ -305,117 +297,22 @@ DEF_OP(Syscall) {
|
||||
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset);
|
||||
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
FillStaticRegs(true, GPRSpillMask, FPRSpillMask, ARMEmitter::Reg::r1, ARMEmitter::Reg::r2);
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
FillStaticRegs(true, GPRSpillMask, FPRSpillMask, ARMEmitter::Reg::r1, ARMEmitter::Reg::r2);
|
||||
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
|
||||
|
||||
PopDynamicRegs();
|
||||
PopDynamicRegs();
|
||||
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NORETURNEDRESULT) != FEXCore::IR::SyscallFlags::NORETURNEDRESULT) {
|
||||
// Move result to its destination register.
|
||||
// Only if `NORETURNEDRESULT` wasn't set, otherwise we might overwrite the CPUState refilled with `FillStaticRegs`
|
||||
mov(ARMEmitter::Size::i64Bit, GetReg(Node), ARMEmitter::Reg::r0);
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto OSABI = CTX->SyscallHandler->GetOSABI();
|
||||
|
||||
DEF_OP(InlineSyscall) {
|
||||
auto Op = IROp->C<IR::IROp_InlineSyscall>();
|
||||
// Arguments are passed as follows:
|
||||
// X8: SyscallNumber - RA INTERSECT
|
||||
// X0: Arg0 & Return
|
||||
// X1: Arg1
|
||||
// X2: Arg2
|
||||
// X3: Arg3
|
||||
// X4: Arg4 - RA INTERSECT
|
||||
// X5: Arg5 - RA INTERSECT
|
||||
// X6: Arg6 - Doesn't exist in x86-64 land. RA INTERSECT
|
||||
|
||||
// One argument is removed from the SyscallArguments::MAX_ARGS since the first argument was syscall number
|
||||
const static std::array<ARMEmitter::XRegister, FEXCore::HLE::SyscallArguments::MAX_ARGS - 1> RegArgs = {
|
||||
{ARMEmitter::XReg::x0, ARMEmitter::XReg::x1, ARMEmitter::XReg::x2, ARMEmitter::XReg::x3, ARMEmitter::XReg::x4, ARMEmitter::XReg::x5}};
|
||||
|
||||
bool Intersects {};
|
||||
// We always need to spill x8 since we can't know if it is live at this SSA location
|
||||
uint32_t SpillMask = 1U << 8;
|
||||
for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS - 1; ++i) {
|
||||
if (Op->Header.Args[i].IsInvalid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto Reg = GetReg(Op->Header.Args[i]);
|
||||
if (Reg == ARMEmitter::Reg::r8 || Reg == ARMEmitter::Reg::r4 || Reg == ARMEmitter::Reg::r5) {
|
||||
|
||||
SpillMask |= (1U << Reg.Idx());
|
||||
Intersects = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Ordering is incredibly important here
|
||||
// We must spill any overlapping registers first THEN claim we are in a syscall without invalidating state at all
|
||||
// Only spill the registers that intersect with our usage
|
||||
SpillStaticRegs(TMP1, false, SpillMask);
|
||||
|
||||
// Now that we are spilled, store in the state that we are in a syscall
|
||||
// Still without overwriting registers that matter
|
||||
// 16bit LoadConstant to be a single instruction
|
||||
// We must always spill at least one register (x8) so this value always has a bit set
|
||||
// This gives the signal handler a value to check to see if we are in a syscall at all
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, SpillMask & 0xFFFF);
|
||||
str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
|
||||
|
||||
// Now that we have claimed to be a syscall we can set up the arguments
|
||||
const auto EmitSize = CTX->Config.Is64BitMode() ? ARMEmitter::Size::i64Bit : ARMEmitter::Size::i32Bit;
|
||||
const auto EmitSubSize = CTX->Config.Is64BitMode() ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i32Bit;
|
||||
if (Intersects) {
|
||||
for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS - 1; ++i) {
|
||||
if (Op->Header.Args[i].IsInvalid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto Reg = GetReg(Op->Header.Args[i]);
|
||||
if (SpillMask & (1U << Reg.Idx())) {
|
||||
// In the case of intersection with x4, x5, or x8 then these are currently SRA
|
||||
// for registers RAX, RDX, and RSP. Which have just been spilled
|
||||
// Just load back from the context.
|
||||
auto Correlation = GetX86RegRelationToARMReg(Reg);
|
||||
LOGMAN_THROW_A_FMT(Correlation != X86State::REG_INVALID, "Invalid register mapping");
|
||||
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[Correlation]));
|
||||
} else {
|
||||
mov(EmitSize, RegArgs[i].R(), Reg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS - 1; ++i) {
|
||||
if (Op->Header.Args[i].IsInvalid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
mov(EmitSize, RegArgs[i].R(), GetReg(Op->Header.Args[i]));
|
||||
}
|
||||
}
|
||||
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r8, Op->HostSyscallNumber);
|
||||
svc(0);
|
||||
// On updated signal mask we can receive a signal RIGHT HERE
|
||||
|
||||
if ((Op->Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
// Now that we are done in the syscall we need to carefully peel back the state
|
||||
// First unspill the registers from before
|
||||
FillStaticRegs(false, SpillMask, ~0U, ARMEmitter::Reg::r8, ARMEmitter::Reg::r1);
|
||||
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
|
||||
|
||||
// Result is now in x0
|
||||
// Move result to its destination register
|
||||
mov(EmitSize, GetReg(Node), ARMEmitter::Reg::r0);
|
||||
if (OSABI != FEXCore::HLE::SyscallOSABI::OS_GENERIC) {
|
||||
// Move result to its destination register.
|
||||
// Only if `NORETURNEDRESULT` wasn't set, otherwise we might overwrite the CPUState refilled with `FillStaticRegs`
|
||||
mov(ARMEmitter::Size::i64Bit, GetReg(Node), ARMEmitter::Reg::r0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,7 +355,7 @@ DEF_OP(ValidateCode) {
|
||||
while (len >= Size) {
|
||||
LoadData();
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, TMP2);
|
||||
cbnz(ARMEmitter::Size::i64Bit, TMP1, &Fail);
|
||||
cbnz_OrRestart(ARMEmitter::Size::i64Bit, TMP1, &Fail);
|
||||
len -= Size;
|
||||
Offset += Size;
|
||||
}
|
||||
@@ -486,10 +383,10 @@ DEF_OP(ValidateCode) {
|
||||
|
||||
ARMEmitter::ForwardLabel End;
|
||||
LoadConstant(ARMEmitter::Size::i32Bit, Dst, 0);
|
||||
b(&End);
|
||||
Bind(&Fail);
|
||||
b_OrRestart(&End);
|
||||
BindOrRestart(&Fail);
|
||||
LoadConstant(ARMEmitter::Size::i32Bit, Dst, 1);
|
||||
Bind(&End);
|
||||
BindOrRestart(&End);
|
||||
}
|
||||
|
||||
DEF_OP(ThreadRemoveCodeEntry) {
|
||||
|
||||
@@ -11,8 +11,6 @@ desc: Main glue logic of the arm64 splatter backend
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Common/SoftFloat.h"
|
||||
|
||||
#include "Interface/Context/Context.h"
|
||||
#include "Interface/Core/LookupCache.h"
|
||||
#include "Interface/Core/Dispatcher/Dispatcher.h"
|
||||
@@ -30,6 +28,7 @@ $end_info$
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/LongJump.h>
|
||||
#include <FEXCore/Utils/Profiler.h>
|
||||
#include <FEXCore/Utils/Telemetry.h>
|
||||
#include <FEXCore/Utils/TypeDefines.h>
|
||||
@@ -37,7 +36,6 @@ $end_info$
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
@@ -495,7 +493,7 @@ void Arm64JITCore::Op_Unhandled(const IR::IROp_Header* IROp, IR::Ref Node) {
|
||||
}
|
||||
}
|
||||
|
||||
static void DirectBlockDelinker(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record, bool Call) {
|
||||
static void DirectBlockDelinker(FEXCore::Context::ExitFunctionLinkData* Record, bool Call) {
|
||||
uintptr_t JumpThunkStartAddress = reinterpret_cast<uintptr_t>(Record) - 0x10;
|
||||
uintptr_t CallerAddress = JumpThunkStartAddress + Record->CallerOffset;
|
||||
auto BranchOffset = JumpThunkStartAddress / 4 - CallerAddress / 4;
|
||||
@@ -513,7 +511,7 @@ static void DirectBlockDelinker(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Co
|
||||
ARMEmitter::Emitter::ClearICache(reinterpret_cast<void*>(CallerAddress), 4);
|
||||
}
|
||||
|
||||
static void IndirectBlockDelinker(FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record) {
|
||||
static void IndirectBlockDelinker(FEXCore::Context::ExitFunctionLinkData* Record) {
|
||||
uintptr_t JumpThunkStartAddress = reinterpret_cast<uintptr_t>(Record) - 0x10;
|
||||
uint32_t BranchInst = 0;
|
||||
ARMEmitter::Emitter BranchEmit(reinterpret_cast<uint8_t*>(&BranchInst), 4);
|
||||
@@ -540,7 +538,7 @@ uint64_t Arm64JITCore::ExitFunctionLink(FEXCore::Core::CpuStateFrame* Frame, FEX
|
||||
// Guard the LookupCache lock with the code invalidation mutex, to avoid issues with forking
|
||||
auto lk_inval =
|
||||
GuardSignalDeferringSection<std::shared_lock>(static_cast<Context::ContextImpl*>(Thread->CTX)->CodeInvalidationMutex, Thread);
|
||||
HostCode = Thread->LookupCache->FindBlock(GuestRip);
|
||||
HostCode = Thread->LookupCache->FindBlock(Thread, GuestRip);
|
||||
}
|
||||
if (!HostCode) {
|
||||
// Hold a reference to the code buffer, to avoid linking unmapped code if compilation triggers a recreation.
|
||||
@@ -565,7 +563,7 @@ uint64_t Arm64JITCore::ExitFunctionLink(FEXCore::Core::CpuStateFrame* Frame, FEX
|
||||
auto lk_inval = GuardSignalDeferringSection<std::shared_lock>(static_cast<Context::ContextImpl*>(Thread->CTX)->CodeInvalidationMutex, Thread);
|
||||
|
||||
// Lock here is necessary to prevent simultaneous linking and delinking
|
||||
auto lk = Thread->LookupCache->AcquireLock();
|
||||
auto lk = Thread->LookupCache->AcquireWriteLock();
|
||||
|
||||
// For non-calls, this would extend into the block's code, however that's fine as an out-of-range adr would never
|
||||
// be generated avoiding any false positives.
|
||||
@@ -578,14 +576,17 @@ uint64_t Arm64JITCore::ExitFunctionLink(FEXCore::Core::CpuStateFrame* Frame, FEX
|
||||
|
||||
if (KnownCallMarkerInst == ExpectedKnownCallMarkerInst) {
|
||||
BranchEmit.bl(BranchOffset);
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, Record, [](FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record) {
|
||||
DirectBlockDelinker(Frame, Record, true);
|
||||
});
|
||||
Thread->LookupCache->AddBlockLink(
|
||||
GuestRip, Record,
|
||||
[](FEXCore::Context::ExitFunctionLinkData* Record) { DirectBlockDelinker(Record, true); }, lk);
|
||||
} else {
|
||||
BranchEmit.b(BranchOffset);
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, Record, [](FEXCore::Core::CpuStateFrame* Frame, FEXCore::Context::ExitFunctionLinkData* Record) {
|
||||
DirectBlockDelinker(Frame, Record, false);
|
||||
});
|
||||
Thread->LookupCache->AddBlockLink(
|
||||
GuestRip, Record,
|
||||
[](FEXCore::Context::ExitFunctionLinkData* Record) {
|
||||
DirectBlockDelinker(Record, false);
|
||||
},
|
||||
lk);
|
||||
}
|
||||
|
||||
std::atomic_ref<uint32_t>(*reinterpret_cast<uint32_t*>(CallerAddress)).store(BranchInst, std::memory_order::relaxed);
|
||||
@@ -604,7 +605,7 @@ uint64_t Arm64JITCore::ExitFunctionLink(FEXCore::Core::CpuStateFrame* Frame, FEX
|
||||
std::atomic_ref<uint32_t>(*reinterpret_cast<uint32_t*>(JumpThunkStartAddress)).store(LdrInst, std::memory_order::relaxed);
|
||||
ARMEmitter::Emitter::ClearICache(reinterpret_cast<void*>(JumpThunkStartAddress), 4);
|
||||
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, Record, IndirectBlockDelinker);
|
||||
Thread->LookupCache->AddBlockLink(GuestRip, Record, IndirectBlockDelinker, lk);
|
||||
}
|
||||
|
||||
return HostCode;
|
||||
@@ -669,15 +670,6 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In
|
||||
|
||||
CurrentCodeBuffer = CodeBuffers.GetLatest();
|
||||
ThreadState->LookupCache->Shared = CurrentCodeBuffer->LookupCache.get();
|
||||
|
||||
// Setup dynamic dispatch.
|
||||
if (ParanoidTSO()) {
|
||||
RT_LoadMemTSO = &Arm64JITCore::Op_ParanoidLoadMemTSO;
|
||||
RT_StoreMemTSO = &Arm64JITCore::Op_ParanoidStoreMemTSO;
|
||||
} else {
|
||||
RT_LoadMemTSO = &Arm64JITCore::Op_LoadMemTSO;
|
||||
RT_StoreMemTSO = &Arm64JITCore::Op_StoreMemTSO;
|
||||
}
|
||||
}
|
||||
|
||||
void Arm64JITCore::EmitDetectionString() {
|
||||
@@ -689,13 +681,13 @@ void Arm64JITCore::EmitDetectionString() {
|
||||
void Arm64JITCore::ClearCache() {
|
||||
// NOTE: Holding on to the reference here is required to ensure validity of the WriteLock mutex
|
||||
auto PrevCodeBuffer = CurrentCodeBuffer;
|
||||
std::lock_guard lk(PrevCodeBuffer->LookupCache->WriteLock);
|
||||
auto lk = PrevCodeBuffer->LookupCache->AcquireWriteLock();
|
||||
|
||||
auto CodeBuffer = GetEmptyCodeBuffer();
|
||||
SetBuffer(CodeBuffer->Ptr, CodeBuffer->Size);
|
||||
EmitDetectionString();
|
||||
|
||||
ThreadState->LookupCache->ChangeGuestToHostMapping(*PrevCodeBuffer, *CurrentCodeBuffer->LookupCache);
|
||||
ThreadState->LookupCache->ChangeGuestToHostMapping(*PrevCodeBuffer, *CurrentCodeBuffer->LookupCache, lk);
|
||||
}
|
||||
|
||||
Arm64JITCore::~Arm64JITCore() {}
|
||||
@@ -748,11 +740,11 @@ void Arm64JITCore::EmitTFCheck() {
|
||||
// Note that this needs to be before the below suspend checks, as X86 checks this flag immediately after executing an instruction.
|
||||
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
|
||||
|
||||
cbz(ARMEmitter::Size::i32Bit, TMP1, &l_TFUnset);
|
||||
(void)cbz(ARMEmitter::Size::i32Bit, TMP1, &l_TFUnset);
|
||||
|
||||
// X86 semantically checks TF after executing each instruction, so e.g. setting a context with TF set will execute a single instruction
|
||||
// and then raise an exception. However on the FEX side this is simpler to implement by checking at the start of each instruction, handle this by having bit 1 being unset in the flag state indicate that TF is blocked for a single instruction.
|
||||
tbz(TMP1, 1, &l_TFBlocked);
|
||||
(void)tbz(TMP1, 1, &l_TFBlocked);
|
||||
|
||||
// Block TF for a single instruction when the frontend jumps to a new context by unsetting bit 1.
|
||||
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
|
||||
@@ -775,11 +767,11 @@ void Arm64JITCore::EmitTFCheck() {
|
||||
ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.GuestSignal_SIGTRAP));
|
||||
br(TMP1);
|
||||
|
||||
Bind(&l_TFBlocked);
|
||||
(void)Bind(&l_TFBlocked);
|
||||
// If TF was blocked for this instruction, unblock it for the next.
|
||||
LoadConstant(ARMEmitter::Size::i32Bit, TMP1, 0b11);
|
||||
strb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
|
||||
Bind(&l_TFUnset);
|
||||
(void)Bind(&l_TFUnset);
|
||||
}
|
||||
|
||||
void Arm64JITCore::EmitSuspendInterruptCheck() {
|
||||
@@ -797,14 +789,14 @@ void Arm64JITCore::EmitSuspendInterruptCheck() {
|
||||
ARMEmitter::ForwardLabel l_NoSuspend;
|
||||
cbz(ARMEmitter::Size::i32Bit, TMP2, &l_NoSuspend);
|
||||
brk(SuspendMagic);
|
||||
Bind(&l_NoSuspend);
|
||||
(void)Bind(&l_NoSuspend);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Arm64JITCore::EmitEntryPoint(ARMEmitter::BackwardLabel& HeaderLabel, bool CheckTF) {
|
||||
// Get the address of the JITCodeHeader and store in to the core state.
|
||||
// Two instruction cost, each 1 cycle.
|
||||
adr(TMP1, &HeaderLabel);
|
||||
adr_OrRestart(TMP1, &HeaderLabel);
|
||||
str(TMP1, STATE, offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader));
|
||||
|
||||
if (CheckTF) {
|
||||
@@ -821,21 +813,32 @@ void Arm64JITCore::EmitEntryPoint(ARMEmitter::BackwardLabel& HeaderLabel, bool C
|
||||
sub(ARMEmitter::Size::i64Bit, ARMEmitter::XReg::rsp, ARMEmitter::XReg::rsp, TMP1, ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
}
|
||||
|
||||
EmitSuspendInterruptCheck();
|
||||
}
|
||||
|
||||
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size, bool SingleInst, const FEXCore::IR::IRListView* IR,
|
||||
FEXCore::Core::DebugData* DebugData, bool CheckTF) {
|
||||
FEXCORE_PROFILE_SCOPED("Arm64::CompileCode");
|
||||
|
||||
JumpTargets.clear();
|
||||
CallReturnTargets.clear();
|
||||
PendingJumpThunks.clear();
|
||||
uint32_t SSACount = IR->GetSSACount();
|
||||
JumpTargets.resize(IR->GetHeader()->BlockCount, {});
|
||||
|
||||
this->Entry = Entry;
|
||||
this->DebugData = DebugData;
|
||||
this->IR = IR;
|
||||
RequiresFarARM64Jumps = false;
|
||||
|
||||
switch (static_cast<RestartOptions::Control>(FEXCore::LongJump::SetJump(RestartControl.RestartJump))) {
|
||||
case RestartOptions::Control::Incoming:
|
||||
// Nothing
|
||||
break;
|
||||
case RestartOptions::Control::EnableFarARM64Jumps: RequiresFarARM64Jumps = true; break;
|
||||
default: ERROR_AND_DIE_FMT("Unhandled Arm64 restart condition!");
|
||||
}
|
||||
|
||||
uint32_t SSACount = IR->GetSSACount();
|
||||
JumpTargets.clear();
|
||||
CallReturnTargets.clear();
|
||||
PendingJumpThunks.clear();
|
||||
JumpTargets.resize(IR->GetHeader()->BlockCount, {});
|
||||
|
||||
CodeData.EntryPoints.clear();
|
||||
|
||||
// Fairly excessive buffer range to make sure we don't overflow
|
||||
@@ -850,7 +853,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
|
||||
// Put the code header at the start of the data block.
|
||||
ARMEmitter::BackwardLabel JITCodeHeaderLabel {};
|
||||
Bind(&JITCodeHeaderLabel);
|
||||
(void)Bind(&JITCodeHeaderLabel);
|
||||
JITCodeHeader* CodeHeader = GetCursorAddress<JITCodeHeader*>();
|
||||
CursorIncrement(sizeof(JITCodeHeader));
|
||||
|
||||
@@ -898,7 +901,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
if (PendingTargetLabel->Backward.Location) {
|
||||
EmitSuspendInterruptCheck();
|
||||
}
|
||||
b(PendingTargetLabel);
|
||||
b_OrRestart(PendingTargetLabel);
|
||||
PendingTargetLabel = nullptr;
|
||||
}
|
||||
|
||||
@@ -908,14 +911,14 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
const auto IsReturnTarget = CallReturnTargets.try_emplace(Node).first;
|
||||
if (PendingTargetLabel) {
|
||||
// If there is a fallthrough branch to this block, skip over the entrypoint code.
|
||||
b(Target);
|
||||
b_OrRestart(Target);
|
||||
} else if (PendingCallReturnTargetLabel && PendingCallReturnTargetLabel != &IsReturnTarget->second) {
|
||||
// If we just emitted a call, but the block we're now emitting is not the return block so don't fallthrough.
|
||||
b(PendingCallReturnTargetLabel);
|
||||
b_OrRestart(PendingCallReturnTargetLabel);
|
||||
}
|
||||
PendingCallReturnTargetLabel = nullptr;
|
||||
|
||||
Bind(&IsReturnTarget->second);
|
||||
BindOrRestart(&IsReturnTarget->second);
|
||||
CodeData.EntryPoints.emplace(BlockStartRIP, GetCursorAddress<uint8_t*>());
|
||||
DebugData->GuestOpcodes.push_back({BlockIROp->GuestEntryOffset, GetCursorAddress<uint8_t*>() - CodeData.BlockBegin});
|
||||
|
||||
@@ -924,18 +927,16 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
|
||||
if (PendingCallReturnTargetLabel) {
|
||||
// If there is still a pending call return target, then the block we're emitting is not the return block so don't fallthrough.
|
||||
b(PendingCallReturnTargetLabel);
|
||||
b_OrRestart(PendingCallReturnTargetLabel);
|
||||
PendingCallReturnTargetLabel = nullptr;
|
||||
}
|
||||
PendingTargetLabel = nullptr;
|
||||
|
||||
Bind(Target);
|
||||
BindOrRestart(Target);
|
||||
}
|
||||
|
||||
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
|
||||
switch (IROp->Op) {
|
||||
#define REGISTER_OP_RT(op, x) \
|
||||
case FEXCore::IR::IROps::OP_##op: std::invoke(RT_##x, this, IROp, CodeNode); break
|
||||
#define REGISTER_OP(op, x) \
|
||||
case FEXCore::IR::IROps::OP_##op: Op_##x(IROp, CodeNode); break
|
||||
|
||||
@@ -956,7 +957,7 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
if (PendingTargetLabel->Backward.Location) {
|
||||
EmitSuspendInterruptCheck();
|
||||
}
|
||||
b(PendingTargetLabel);
|
||||
b_OrRestart(PendingTargetLabel);
|
||||
}
|
||||
PendingTargetLabel = nullptr;
|
||||
|
||||
@@ -967,21 +968,21 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
|
||||
ARMEmitter::ForwardLabel l_DoLink;
|
||||
uint64_t ThunkAddress = GetCursorAddress<uint64_t>();
|
||||
Bind(&PendingJumpThunk.Label);
|
||||
b(&l_DoLink);
|
||||
BindOrRestart(&PendingJumpThunk.Label);
|
||||
b_OrRestart(&l_DoLink);
|
||||
br(TMP1);
|
||||
Bind(&l_DoLink);
|
||||
BindOrRestart(&l_DoLink);
|
||||
ldr(TMP1, &l_ExitLink);
|
||||
blr(TMP1);
|
||||
|
||||
// This is a ExitFunctionLinkData struct
|
||||
Bind(&l_ExitLink);
|
||||
BindOrRestart(&l_ExitLink);
|
||||
dc64(0); // HostCode
|
||||
dc64(PendingJumpThunk.GuestRIP); // GuestRIP
|
||||
dc64(PendingJumpThunk.CallerAddress - ThunkAddress); // CallerOffset
|
||||
}
|
||||
|
||||
Bind(&l_ExitLink);
|
||||
BindOrRestart(&l_ExitLink);
|
||||
dc64(ThreadState->CurrentFrame->Pointers.Common.ExitFunctionLinker);
|
||||
|
||||
// CodeSize not including the header or tail data.
|
||||
@@ -1065,7 +1066,8 @@ CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry, uint64_t Size
|
||||
"doesn't match up!\n");
|
||||
if (auto Prev = CheckCodeBufferUpdate()) {
|
||||
Allocator::VirtualDontNeed(ThreadState->CallRetStackBase, FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE);
|
||||
ThreadState->LookupCache->ChangeGuestToHostMapping(*Prev, *CurrentCodeBuffer->LookupCache);
|
||||
auto lk = ThreadState->LookupCache->AcquireWriteLock();
|
||||
ThreadState->LookupCache->ChangeGuestToHostMapping(*Prev, *CurrentCodeBuffer->LookupCache, lk);
|
||||
}
|
||||
|
||||
// NOTE: 16-byte alignment of the new cursor offset must be preserved for block linking records
|
||||
|
||||
@@ -23,6 +23,7 @@ $end_info$
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
#include <FEXCore/fextl/string.h>
|
||||
#include <FEXCore/fextl/vector.h>
|
||||
#include <FEXCore/Utils/LongJump.h>
|
||||
|
||||
#include <CodeEmitter/Emitter.h>
|
||||
|
||||
@@ -60,14 +61,25 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
|
||||
const bool HostSupportsSVE128 {};
|
||||
const bool HostSupportsSVE256 {};
|
||||
const bool HostSupportsAVX256 {};
|
||||
const bool HostSupportsRPRES {};
|
||||
const bool HostSupportsAFP {};
|
||||
|
||||
struct RestartOptions {
|
||||
FEXCore::LongJump::JumpBuf RestartJump;
|
||||
enum class Control : uint64_t {
|
||||
Incoming = 0,
|
||||
EnableFarARM64Jumps = 1,
|
||||
};
|
||||
};
|
||||
|
||||
// FEXCore makes assumptions in the JIT about certain conditions being true.
|
||||
// In the rare case when those assumptions are broken, FEX needs to safely restart the JIT.
|
||||
RestartOptions RestartControl {};
|
||||
bool RequiresFarARM64Jumps {};
|
||||
|
||||
ARMEmitter::BiDirectionalLabel* PendingTargetLabel {};
|
||||
ARMEmitter::BiDirectionalLabel* PendingCallReturnTargetLabel {};
|
||||
FEXCore::Context::ContextImpl* CTX {};
|
||||
@@ -331,14 +343,179 @@ private:
|
||||
void EmitLinkedBranch(uint64_t GuestRIP, bool Call) {
|
||||
PendingJumpThunks.push_back({GetCursorAddress<uint64_t>(), GuestRIP, {}});
|
||||
auto& Thunk = PendingJumpThunks.back();
|
||||
Bind(&Thunk.Label);
|
||||
BindOrRestart(&Thunk.Label);
|
||||
if (Call) {
|
||||
bl(&Thunk.Label);
|
||||
bl_OrRestart(&Thunk.Label);
|
||||
} else {
|
||||
b(&Thunk.Label);
|
||||
b_OrRestart(&Thunk.Label);
|
||||
}
|
||||
}
|
||||
|
||||
// Restart helpers
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void bl_OrRestart(T* Label) {
|
||||
if (bl(Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can support this but currently unnecessary.
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void b_OrRestart(T* Label) {
|
||||
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can support this but currently unnecessary.
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void b_OrRestart(ARMEmitter::Condition Cond, T* Label) {
|
||||
if (RequiresFarARM64Jumps) {
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
|
||||
(void)b(InvertCondition(Cond), &Skip);
|
||||
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
}
|
||||
|
||||
(void)Bind(&Skip);
|
||||
return;
|
||||
}
|
||||
|
||||
if (b(Cond, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void cbz_OrRestart(ARMEmitter::Size s, ARMEmitter::Register rt, T* Label) {
|
||||
if (RequiresFarARM64Jumps) {
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
|
||||
(void)cbnz(s, rt, &Skip);
|
||||
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
}
|
||||
|
||||
(void)Bind(&Skip);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cbz(s, rt, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void cbnz_OrRestart(ARMEmitter::Size s, ARMEmitter::Register rt, T* Label) {
|
||||
if (RequiresFarARM64Jumps) {
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
|
||||
(void)cbz(s, rt, &Skip);
|
||||
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
}
|
||||
|
||||
(void)Bind(&Skip);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cbnz(s, rt, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void tbz_OrRestart(ARMEmitter::Register rt, uint32_t Bit, T* Label) {
|
||||
if (RequiresFarARM64Jumps) {
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
|
||||
(void)tbnz(rt, Bit, &Skip);
|
||||
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
}
|
||||
|
||||
(void)Bind(&Skip);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tbz(rt, Bit, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void tbnz_OrRestart(ARMEmitter::Register rt, uint32_t Bit, T* Label) {
|
||||
if (RequiresFarARM64Jumps) {
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
// Wrap a manual Cond check around an unconditional branch; this can encode larger offsets
|
||||
(void)tbz(rt, Bit, &Skip);
|
||||
if (b(Label) == ARMEmitter::BranchEncodeSucceeded::Failure) {
|
||||
ERROR_AND_DIE_FMT("Tried to branch larger than 128MB away!");
|
||||
}
|
||||
|
||||
(void)Bind(&Skip);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tbnz(rt, Bit, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void adr_OrRestart(ARMEmitter::Register rd, T* Label) {
|
||||
if (adr(rd, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can support this but currently unnecessary.
|
||||
ERROR_AND_DIE_FMT("Long ADR currently unsupported!");
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void adrp_OrRestart(ARMEmitter::Register rd, T* Label) {
|
||||
if (adrp(rd, Label) == ARMEmitter::BranchEncodeSucceeded::Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can support this but currently unnecessary.
|
||||
ERROR_AND_DIE_FMT("Long ADRP currently unsupported!");
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
template<ARMEmitter::IsLabel T>
|
||||
void BindOrRestart(T* Label) {
|
||||
if (Bind(Label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RequiresFarARM64Jumps) {
|
||||
// This should have been caught before this point.
|
||||
ERROR_AND_DIE_FMT("Unhandled long bind");
|
||||
return;
|
||||
}
|
||||
|
||||
FEXCore::LongJump::LongJump(RestartControl.RestartJump, FEXCore::ToUnderlying(RestartOptions::Control::EnableFarARM64Jumps));
|
||||
}
|
||||
|
||||
// This is purely a debugging aid for developers to see if they are in JIT code space when inspecting raw memory
|
||||
void EmitDetectionString();
|
||||
IR::RegisterAllocationPass* RAPass {};
|
||||
@@ -430,17 +607,8 @@ private:
|
||||
|
||||
void EmitEntryPoint(ARMEmitter::BackwardLabel& HeaderLabel, bool CheckTF);
|
||||
|
||||
// Runtime selection;
|
||||
// Load and store TSO memory style
|
||||
OpType RT_LoadMemTSO;
|
||||
OpType RT_StoreMemTSO;
|
||||
|
||||
#define DEF_OP(x) void Op_##x(IR::IROp_Header const* IROp, IR::Ref Node)
|
||||
|
||||
// Dynamic Dispatcher supporting operations
|
||||
DEF_OP(ParanoidLoadMemTSO);
|
||||
DEF_OP(ParanoidStoreMemTSO);
|
||||
|
||||
///< Unhandled handler
|
||||
DEF_OP(Unhandled);
|
||||
|
||||
|
||||
@@ -912,7 +912,7 @@ DEF_OP(VLoadVectorMasked) {
|
||||
|
||||
// If the sign bit is zero then skip the load
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
(void)tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
// Do the gather load for this element into the destination
|
||||
switch (IROp->ElementSize) {
|
||||
case IR::OpSize::i8Bit: ld1<ARMEmitter::SubRegSize::i8Bit>(TempDst.Q(), i, TempMemReg); break;
|
||||
@@ -923,7 +923,7 @@ DEF_OP(VLoadVectorMasked) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, IROp->ElementSize); return;
|
||||
}
|
||||
|
||||
Bind(&Skip);
|
||||
(void)Bind(&Skip);
|
||||
|
||||
if ((i + 1) != NumElements) {
|
||||
// Handle register rename to save a move.
|
||||
@@ -1013,7 +1013,7 @@ DEF_OP(VStoreVectorMasked) {
|
||||
|
||||
// If the sign bit is zero then skip the load
|
||||
ARMEmitter::ForwardLabel Skip {};
|
||||
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
(void)tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
// Do the gather load for this element into the destination
|
||||
switch (IROp->ElementSize) {
|
||||
case IR::OpSize::i8Bit: st1<ARMEmitter::SubRegSize::i8Bit>(RegData.Q(), i, TempMemReg); break;
|
||||
@@ -1024,7 +1024,7 @@ DEF_OP(VStoreVectorMasked) {
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, IROp->ElementSize); return;
|
||||
}
|
||||
|
||||
Bind(&Skip);
|
||||
(void)Bind(&Skip);
|
||||
|
||||
if ((i + 1) != NumElements) {
|
||||
// Handle register rename to save a move.
|
||||
@@ -1102,7 +1102,7 @@ void Arm64JITCore::Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize,
|
||||
PerformMove(ElementSize, WorkingReg, MaskReg, i);
|
||||
|
||||
// Skip if the mask's sign bit isn't set
|
||||
tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
(void)tbz(WorkingReg, ElementSizeInBits - 1, &Skip);
|
||||
|
||||
// Extract Index Element
|
||||
if ((IndexElement * IR::OpSizeToSize(VectorIndexSize)) >= 16) {
|
||||
@@ -1140,7 +1140,7 @@ void Arm64JITCore::Emulate128BitGather(IR::OpSize Size, IR::OpSize ElementSize,
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, ElementSize); FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
Bind(&Skip);
|
||||
(void)Bind(&Skip);
|
||||
}
|
||||
|
||||
if (NeedsDestTmp) {
|
||||
@@ -1874,7 +1874,7 @@ DEF_OP(MemSet) {
|
||||
|
||||
if (!DirectionIsInline) {
|
||||
// Backward or forwards implementation depends on flag
|
||||
tbnz(DirectionReg, 1, &BackwardImpl);
|
||||
(void)tbnz(DirectionReg, 1, &BackwardImpl);
|
||||
}
|
||||
|
||||
auto MemStore = [this](auto Value, uint32_t OpSize, int32_t Size) {
|
||||
@@ -1922,7 +1922,7 @@ DEF_OP(MemSet) {
|
||||
ARMEmitter::ForwardLabel DoneInternal {};
|
||||
|
||||
// Early exit if zero count.
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
if (!IsAtomic) {
|
||||
ARMEmitter::ForwardLabel AgainInternal256Exit {};
|
||||
@@ -1939,50 +1939,50 @@ DEF_OP(MemSet) {
|
||||
// Do this in two parts, to fallback to the byte by byte loop if size < 32, and to the
|
||||
// single copy loop if size < 64.
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
|
||||
// Fill VTMP2 with the set pattern
|
||||
dup(SubRegSize, VTMP2.Q(), Value);
|
||||
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbnz(TMP1, 63, &AgainInternal256Exit);
|
||||
(void)tbnz(TMP1, 63, &AgainInternal256Exit);
|
||||
|
||||
Bind(&AgainInternal256);
|
||||
(void)Bind(&AgainInternal256);
|
||||
stp<ARMEmitter::IndexType::POST>(VTMP2.Q(), VTMP2.Q(), TMP2, 32 * Direction);
|
||||
stp<ARMEmitter::IndexType::POST>(VTMP2.Q(), VTMP2.Q(), TMP2, 32 * Direction);
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
|
||||
tbz(TMP1, 63, &AgainInternal256);
|
||||
(void)tbz(TMP1, 63, &AgainInternal256);
|
||||
|
||||
Bind(&AgainInternal256Exit);
|
||||
(void)Bind(&AgainInternal256Exit);
|
||||
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
Bind(&AgainInternal128);
|
||||
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
(void)Bind(&AgainInternal128);
|
||||
stp<ARMEmitter::IndexType::POST>(VTMP2.Q(), VTMP2.Q(), TMP2, 32 * Direction);
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbz(TMP1, 63, &AgainInternal128);
|
||||
(void)tbz(TMP1, 63, &AgainInternal128);
|
||||
|
||||
Bind(&AgainInternal128Exit);
|
||||
(void)Bind(&AgainInternal128Exit);
|
||||
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
if (Direction == -1) {
|
||||
add(ARMEmitter::Size::i64Bit, TMP2, TMP2, 32 - Size);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&AgainInternal);
|
||||
(void)Bind(&AgainInternal);
|
||||
if (IsAtomic) {
|
||||
MemStoreTSO(Value, OpSize, SizeDirection);
|
||||
} else {
|
||||
MemStore(Value, OpSize, SizeDirection);
|
||||
}
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
|
||||
cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
|
||||
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
|
||||
|
||||
Bind(&DoneInternal);
|
||||
(void)Bind(&DoneInternal);
|
||||
|
||||
if (SizeDirection >= 0) {
|
||||
switch (OpSize) {
|
||||
@@ -2012,12 +2012,12 @@ DEF_OP(MemSet) {
|
||||
EmitMemset(Direction);
|
||||
|
||||
if (Direction == 1) {
|
||||
b(&Done);
|
||||
Bind(&BackwardImpl);
|
||||
(void)b(&Done);
|
||||
(void)Bind(&BackwardImpl);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&Done);
|
||||
(void)Bind(&Done);
|
||||
// Destination already set to the final pointer.
|
||||
}
|
||||
}
|
||||
@@ -2067,7 +2067,7 @@ DEF_OP(MemCpy) {
|
||||
|
||||
if (!DirectionIsInline) {
|
||||
// Backward or forwards implementation depends on flag
|
||||
tbnz(DirectionReg, 1, &BackwardImpl);
|
||||
(void)tbnz(DirectionReg, 1, &BackwardImpl);
|
||||
}
|
||||
|
||||
auto MemCpy = [this](uint32_t OpSize, int32_t Size) {
|
||||
@@ -2164,7 +2164,7 @@ DEF_OP(MemCpy) {
|
||||
ARMEmitter::ForwardLabel DoneInternal {};
|
||||
|
||||
// Early exit if zero count.
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
if (!IsAtomic) {
|
||||
ARMEmitter::ForwardLabel AbsPos {};
|
||||
@@ -2174,11 +2174,11 @@ DEF_OP(MemCpy) {
|
||||
ARMEmitter::BackwardLabel AgainInternal256 {};
|
||||
|
||||
sub(ARMEmitter::Size::i64Bit, TMP4, TMP2, TMP3);
|
||||
tbz(TMP4, 63, &AbsPos);
|
||||
(void)tbz(TMP4, 63, &AbsPos);
|
||||
neg(ARMEmitter::Size::i64Bit, TMP4, TMP4);
|
||||
Bind(&AbsPos);
|
||||
(void)Bind(&AbsPos);
|
||||
sub(ARMEmitter::Size::i64Bit, TMP4, TMP4, 32);
|
||||
tbnz(TMP4, 63, &AgainInternal);
|
||||
(void)tbnz(TMP4, 63, &AgainInternal);
|
||||
|
||||
if (Direction == -1) {
|
||||
sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, 32 - Size);
|
||||
@@ -2190,30 +2190,30 @@ DEF_OP(MemCpy) {
|
||||
// Do this in two parts, to fallback to the byte by byte loop if size < 32, and to the
|
||||
// single copy loop if size < 64.
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbnz(TMP1, 63, &AgainInternal256Exit);
|
||||
(void)tbnz(TMP1, 63, &AgainInternal256Exit);
|
||||
|
||||
Bind(&AgainInternal256);
|
||||
(void)Bind(&AgainInternal256);
|
||||
MemCpy(32, 32 * Direction);
|
||||
MemCpy(32, 32 * Direction);
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
|
||||
tbz(TMP1, 63, &AgainInternal256);
|
||||
(void)tbz(TMP1, 63, &AgainInternal256);
|
||||
|
||||
Bind(&AgainInternal256Exit);
|
||||
(void)Bind(&AgainInternal256Exit);
|
||||
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 64 / Size);
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
Bind(&AgainInternal128);
|
||||
(void)tbnz(TMP1, 63, &AgainInternal128Exit);
|
||||
(void)Bind(&AgainInternal128);
|
||||
MemCpy(32, 32 * Direction);
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
tbz(TMP1, 63, &AgainInternal128);
|
||||
(void)tbz(TMP1, 63, &AgainInternal128);
|
||||
|
||||
Bind(&AgainInternal128Exit);
|
||||
(void)Bind(&AgainInternal128Exit);
|
||||
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, 32 / Size);
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
(void)cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
if (Direction == -1) {
|
||||
add(ARMEmitter::Size::i64Bit, TMP2, TMP2, 32 - Size);
|
||||
@@ -2221,16 +2221,16 @@ DEF_OP(MemCpy) {
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&AgainInternal);
|
||||
(void)Bind(&AgainInternal);
|
||||
if (IsAtomic) {
|
||||
MemCpyTSO(OpSize, SizeDirection);
|
||||
} else {
|
||||
MemCpy(OpSize, SizeDirection);
|
||||
}
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
|
||||
cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
|
||||
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
|
||||
|
||||
Bind(&DoneInternal);
|
||||
(void)Bind(&DoneInternal);
|
||||
|
||||
// Needs to use temporaries just in case of overwrite
|
||||
mov(TMP1, MemRegDest.X());
|
||||
@@ -2288,186 +2288,15 @@ DEF_OP(MemCpy) {
|
||||
for (int32_t Direction : {1, -1}) {
|
||||
EmitMemcpy(Direction);
|
||||
if (Direction == 1) {
|
||||
b(&Done);
|
||||
Bind(&BackwardImpl);
|
||||
(void)b(&Done);
|
||||
(void)Bind(&BackwardImpl);
|
||||
}
|
||||
}
|
||||
Bind(&Done);
|
||||
(void)Bind(&Done);
|
||||
// Destination already set to the final pointer.
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(ParanoidLoadMemTSO) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadMemTSO>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
auto MemReg = GetReg(Op->Addr);
|
||||
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
uint64_t Offset = 0;
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
if (!IsInlineConstant(Op->Offset, &Offset)) {
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
}
|
||||
}
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
const auto Dst = GetReg(Node);
|
||||
ldapurb(Dst, MemReg, Offset);
|
||||
} else {
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i16Bit: ldapurh(Dst, MemReg, Offset); break;
|
||||
case IR::OpSize::i32Bit: ldapur(Dst.W(), MemReg, Offset); break;
|
||||
case IR::OpSize::i64Bit: ldapur(Dst.X(), MemReg, Offset); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (CTX->HostFeatures.SupportsRCPC && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
ldaprb(Dst.W(), MemReg);
|
||||
} else {
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i16Bit: ldaprh(Dst.W(), MemReg); break;
|
||||
case IR::OpSize::i32Bit: ldapr(Dst.W(), MemReg); break;
|
||||
case IR::OpSize::i64Bit: ldapr(Dst.X(), MemReg); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Dst = GetReg(Node);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit: ldarb(Dst, MemReg); break;
|
||||
case IR::OpSize::i16Bit: ldarh(Dst, MemReg); break;
|
||||
case IR::OpSize::i32Bit: ldar(Dst.W(), MemReg); break;
|
||||
case IR::OpSize::i64Bit: ldar(Dst.X(), MemReg); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
} else {
|
||||
const auto Dst = GetVReg(Node);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit:
|
||||
ldarb(TMP1, MemReg);
|
||||
fmov(ARMEmitter::Size::i32Bit, Dst.S(), TMP1.W());
|
||||
break;
|
||||
case IR::OpSize::i16Bit:
|
||||
ldarh(TMP1, MemReg);
|
||||
fmov(ARMEmitter::Size::i32Bit, Dst.S(), TMP1.W());
|
||||
break;
|
||||
case IR::OpSize::i32Bit:
|
||||
ldar(TMP1.W(), MemReg);
|
||||
fmov(ARMEmitter::Size::i32Bit, Dst.S(), TMP1.W());
|
||||
break;
|
||||
case IR::OpSize::i64Bit:
|
||||
ldar(TMP1, MemReg);
|
||||
fmov(ARMEmitter::Size::i64Bit, Dst.D(), TMP1);
|
||||
break;
|
||||
case IR::OpSize::i128Bit:
|
||||
ldaxp(ARMEmitter::Size::i64Bit, TMP1, TMP2, MemReg);
|
||||
clrex();
|
||||
ins(ARMEmitter::SubRegSize::i64Bit, Dst, 0, TMP1);
|
||||
ins(ARMEmitter::SubRegSize::i64Bit, Dst, 1, TMP2);
|
||||
break;
|
||||
case IR::OpSize::i256Bit:
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use {} with 256-bit operation", __func__);
|
||||
dmb(ARMEmitter::BarrierScope::ISH);
|
||||
ld1b<ARMEmitter::SubRegSize::i8Bit>(Dst.Z(), PRED_TMP_32B.Zeroing(), MemReg);
|
||||
dmb(ARMEmitter::BarrierScope::ISH);
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidLoadMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(ParanoidStoreMemTSO) {
|
||||
const auto Op = IROp->C<IR::IROp_StoreMemTSO>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
auto MemReg = GetReg(Op->Addr);
|
||||
|
||||
if (CTX->HostFeatures.SupportsTSOImm9 && Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
uint64_t Offset = 0;
|
||||
if (!Op->Offset.IsInvalid()) {
|
||||
if (!IsInlineConstant(Op->Offset, &Offset)) {
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP1, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
}
|
||||
}
|
||||
|
||||
if (OpSize == IR::OpSize::i8Bit) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
stlurb(Src, MemReg, Offset);
|
||||
} else {
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i16Bit: stlurh(Src, MemReg, Offset); break;
|
||||
case IR::OpSize::i32Bit: stlur(Src.W(), MemReg, Offset); break;
|
||||
case IR::OpSize::i64Bit: stlur(Src.X(), MemReg, Offset); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
} else if (Op->Class == IR::RegClass::GPR) {
|
||||
const auto Src = GetZeroableReg(Op->Value);
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP1, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit: stlrb(Src, MemReg); break;
|
||||
case IR::OpSize::i16Bit: stlrh(Src, MemReg); break;
|
||||
case IR::OpSize::i32Bit: stlr(Src.W(), MemReg); break;
|
||||
case IR::OpSize::i64Bit: stlr(Src.X(), MemReg); break;
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
} else {
|
||||
const auto Src = GetVReg(Op->Value);
|
||||
|
||||
MemReg = ApplyMemOperand(OpSize, MemReg, TMP4, Op->Offset, Op->OffsetType, Op->OffsetScale);
|
||||
|
||||
switch (OpSize) {
|
||||
case IR::OpSize::i8Bit:
|
||||
umov<ARMEmitter::SubRegSize::i8Bit>(TMP1, Src, 0);
|
||||
stlrb(TMP1, MemReg);
|
||||
break;
|
||||
case IR::OpSize::i16Bit:
|
||||
umov<ARMEmitter::SubRegSize::i16Bit>(TMP1, Src, 0);
|
||||
stlrh(TMP1, MemReg);
|
||||
break;
|
||||
case IR::OpSize::i32Bit:
|
||||
umov<ARMEmitter::SubRegSize::i32Bit>(TMP1, Src, 0);
|
||||
stlr(TMP1.W(), MemReg);
|
||||
break;
|
||||
case IR::OpSize::i64Bit:
|
||||
umov<ARMEmitter::SubRegSize::i64Bit>(TMP1, Src, 0);
|
||||
stlr(TMP1, MemReg);
|
||||
break;
|
||||
case IR::OpSize::i128Bit: {
|
||||
// Move vector to GPRs
|
||||
umov<ARMEmitter::SubRegSize::i64Bit>(TMP1, Src, 0);
|
||||
umov<ARMEmitter::SubRegSize::i64Bit>(TMP2, Src, 1);
|
||||
ARMEmitter::BackwardLabel B;
|
||||
Bind(&B);
|
||||
|
||||
// ldaxp must not have both the destination registers be the same
|
||||
ldaxp(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::zr, TMP3, MemReg); // <- Can hit SIGBUS. Overwritten with DMB
|
||||
stlxp(ARMEmitter::Size::i64Bit, TMP3, TMP1, TMP2, MemReg); // <- Can also hit SIGBUS
|
||||
cbnz(ARMEmitter::Size::i64Bit, TMP3, &B); // < Overwritten with DMB
|
||||
break;
|
||||
}
|
||||
case IR::OpSize::i256Bit: {
|
||||
LOGMAN_THROW_A_FMT(HostSupportsSVE256, "Need SVE256 support in order to use {} with 256-bit operation", __func__);
|
||||
dmb(ARMEmitter::BarrierScope::ISH);
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(Src.Z(), PRED_TMP_32B, MemReg, 0);
|
||||
dmb(ARMEmitter::BarrierScope::ISH);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled ParanoidStoreMemTSO size: {}", OpSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClear) {
|
||||
if (!CTX->HostFeatures.SupportsCacheMaintenanceOps) {
|
||||
dmb(ARMEmitter::BarrierScope::SY);
|
||||
|
||||
@@ -15,7 +15,7 @@ $end_info$
|
||||
|
||||
namespace FEXCore {
|
||||
GuestToHostMap::GuestToHostMap()
|
||||
: BlockLinks_mbr {fextl::pmr::get_default_resource()} {
|
||||
: BlockLinks_mbr {"FEXMem_BlockLinks"} {
|
||||
BlockLinks_pma = fextl::make_unique<std::pmr::polymorphic_allocator<std::byte>>(&BlockLinks_mbr);
|
||||
// Setup our PMR map.
|
||||
BlockLinks = BlockLinks_pma->new_object<BlockLinksMapType>();
|
||||
@@ -24,7 +24,7 @@ GuestToHostMap::GuestToHostMap()
|
||||
LookupCache::LookupCache(FEXCore::Context::ContextImpl* CTX)
|
||||
: ctx {CTX} {
|
||||
|
||||
TotalCacheSize = ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE + L1_SIZE;
|
||||
TotalCacheSize = ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8 + CODE_SIZE + MAX_L1_SIZE;
|
||||
|
||||
// Block cache ends up looking like this
|
||||
// PageMemoryMap[VirtualMemoryRegion >> 12]
|
||||
@@ -39,6 +39,8 @@ LookupCache::LookupCache(FEXCore::Context::ContextImpl* CTX)
|
||||
// We need one pointer per page of virtual memory
|
||||
// At 64GB of virtual memory this will allocate 128MB of virtual memory space
|
||||
PagePointer = reinterpret_cast<uintptr_t>(FEXCore::Allocator::VirtualAlloc(TotalCacheSize, false, false));
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Lookup", reinterpret_cast<void*>(PagePointer),
|
||||
ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8 + CODE_SIZE);
|
||||
CTX->SyscallHandler->MarkOvercommitRange(PagePointer, TotalCacheSize);
|
||||
|
||||
// Allocate our memory backing our pages
|
||||
@@ -46,14 +48,24 @@ LookupCache::LookupCache(FEXCore::Context::ContextImpl* CTX)
|
||||
// XXX: We can drop down to 16KB if we store 4byte offsets from the code base
|
||||
// We currently limit to 128MB of real memory for caching for the total cache size.
|
||||
// Can end up being inefficient if we compile a small number of blocks per page
|
||||
PageMemory = PagePointer + ctx->Config.VirtualMemSize / 4096 * 8;
|
||||
PageMemory = PagePointer + ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8;
|
||||
LOGMAN_THROW_A_FMT(PageMemory != -1ULL, "Failed to allocate page memory");
|
||||
|
||||
// L1 Cache
|
||||
L1Pointer = PageMemory + CODE_SIZE;
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Lookup_L1", reinterpret_cast<void*>(L1Pointer), MAX_L1_SIZE);
|
||||
|
||||
LOGMAN_THROW_A_FMT(L1Pointer != -1ULL, "Failed to allocate L1Pointer");
|
||||
|
||||
VirtualMemSize = ctx->Config.VirtualMemSize;
|
||||
|
||||
if (DynamicL1Cache()) {
|
||||
// Start at minimum size when dynamic.
|
||||
L1PointerMask = MIN_L1_ENTRIES - 1;
|
||||
} else {
|
||||
// Start at maximum instead.
|
||||
L1PointerMask = MAX_L1_ENTRIES - 1;
|
||||
}
|
||||
}
|
||||
|
||||
LookupCache::~LookupCache() {
|
||||
@@ -64,31 +76,27 @@ LookupCache::~LookupCache() {
|
||||
// These will get freed when their memory allocators are deallocated.
|
||||
}
|
||||
|
||||
void LookupCache::ClearL2Cache() {
|
||||
auto lk = Shared->AcquireLock();
|
||||
void LookupCache::ClearL2Cache(const FEXCore::LookupCacheWriteLockToken& lk) {
|
||||
// Clear out the page memory
|
||||
// PagePointer and PageMemory are sequential with each other. Clear both at once.
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE, false);
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer),
|
||||
ctx->Config.VirtualMemSize / FEXCore::Utils::FEX_PAGE_SIZE * 8 + CODE_SIZE, false);
|
||||
AllocateOffset = 0;
|
||||
}
|
||||
|
||||
void LookupCache::ClearThreadLocalCaches() {
|
||||
auto lk = Shared->AcquireLock();
|
||||
|
||||
void LookupCache::ClearThreadLocalCaches(const LookupCacheWriteLockToken&) {
|
||||
// Clear L1 and L2 by clearing the full cache.
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), TotalCacheSize, false);
|
||||
CachedCodePages.clear();
|
||||
}
|
||||
|
||||
void LookupCache::ClearCache() {
|
||||
auto lk = Shared->AcquireLock();
|
||||
|
||||
void LookupCache::ClearCache(const LookupCacheWriteLockToken& lk) {
|
||||
// Clear L1 and L2 by clearing the full cache.
|
||||
FEXCore::Allocator::VirtualDontNeed(reinterpret_cast<void*>(PagePointer), TotalCacheSize, false);
|
||||
|
||||
ClearThreadLocalCaches(lk);
|
||||
Shared->ClearCache(lk);
|
||||
}
|
||||
|
||||
void GuestToHostMap::ClearCache(const LockToken&) {
|
||||
void GuestToHostMap::ClearCache(const LookupCacheWriteLockToken&) {
|
||||
// Allocate a new pointer from the BlockLinks pma again.
|
||||
BlockLinks = BlockLinks_pma->new_object<BlockLinksMapType>();
|
||||
// All code is gone, clear the block list
|
||||
|
||||
@@ -2,30 +2,36 @@
|
||||
#pragma once
|
||||
#include "Interface/Context/Context.h"
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/SHMStats.h>
|
||||
#include <FEXCore/fextl/map.h>
|
||||
#include <FEXCore/fextl/memory_resource.h>
|
||||
#include <FEXCore/fextl/robin_map.h>
|
||||
#include <FEXCore/fextl/vector.h>
|
||||
#include <FEXCore/fextl/unordered_set.h>
|
||||
#include <FEXCore/fextl/memory_resource.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <stddef.h>
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
struct GuestToHostMap {
|
||||
std::recursive_mutex WriteLock;
|
||||
struct LookupCacheWriteLockToken {
|
||||
private:
|
||||
// Only constructible by GuestToHostMap
|
||||
friend struct GuestToHostMap;
|
||||
LookupCacheWriteLockToken(std::mutex& Mutex)
|
||||
: Lock {Mutex} {}
|
||||
std::lock_guard<std::mutex> Lock;
|
||||
};
|
||||
|
||||
struct LockToken {
|
||||
std::lock_guard<std::recursive_mutex> Lock;
|
||||
};
|
||||
struct GuestToHostMap {
|
||||
std::mutex WriteLock;
|
||||
|
||||
[[nodiscard]]
|
||||
LockToken AcquireLock() {
|
||||
return LockToken {std::lock_guard {WriteLock}};
|
||||
LookupCacheWriteLockToken AcquireWriteLock() {
|
||||
return LookupCacheWriteLockToken {WriteLock};
|
||||
}
|
||||
|
||||
struct BlockLinkTag {
|
||||
@@ -49,53 +55,72 @@ struct GuestToHostMap {
|
||||
// walking each block member and destructing objects.
|
||||
//
|
||||
// This makes `BlockLinks` look like a raw pointer that could memory leak, but since it is backed by the MBR, it won't.
|
||||
std::pmr::monotonic_buffer_resource BlockLinks_mbr;
|
||||
fextl::pmr::named_monotonic_page_buffer_resource BlockLinks_mbr;
|
||||
using BlockLinksMapType = std::pmr::map<BlockLinkTag, FEXCore::Context::BlockDelinkerFunc>;
|
||||
fextl::unique_ptr<std::pmr::polymorphic_allocator<std::byte>> BlockLinks_pma;
|
||||
BlockLinksMapType* BlockLinks;
|
||||
|
||||
fextl::robin_map<uint64_t, uint64_t> BlockList;
|
||||
struct BlockEntry {
|
||||
uint64_t HostCode;
|
||||
fextl::vector<uint64_t> CodePages;
|
||||
};
|
||||
|
||||
fextl::robin_map<uint64_t, BlockEntry> BlockList;
|
||||
|
||||
fextl::map<uint64_t, fextl::vector<uint64_t>> CodePages;
|
||||
|
||||
GuestToHostMap();
|
||||
|
||||
// Adds to Guest -> Host code mapping
|
||||
void AddBlockMapping(uint64_t Address, void* HostCode, const LockToken&) {
|
||||
const BlockEntry& AddBlockMapping(uint64_t Address, const fextl::vector<uint64_t>& CodePages, void* HostCode, const LookupCacheWriteLockToken&) {
|
||||
// This may replace an existing mapping
|
||||
// NOTE: Generally no previous entry should exist, however there is one exception:
|
||||
// If the backend updates the active thread's CodeBuffer, the new associated LookupCache
|
||||
// may already contain the block address. Since is comparatively rare, we'll just leak
|
||||
// one of the two blocks in this case.
|
||||
BlockList[Address] = (uintptr_t)HostCode;
|
||||
return BlockList.insert_or_assign(Address, BlockEntry {(uintptr_t)HostCode, CodePages}).first->second;
|
||||
}
|
||||
|
||||
std::optional<uintptr_t> FindBlock(uint64_t Address, const LockToken&) {
|
||||
const BlockEntry* FindBlock(uint64_t Address, const LookupCacheWriteLockToken&) {
|
||||
auto HostCode = BlockList.find(Address);
|
||||
if (HostCode == BlockList.end()) {
|
||||
return std::nullopt;
|
||||
return nullptr;
|
||||
}
|
||||
return HostCode->second;
|
||||
return &HostCode->second;
|
||||
}
|
||||
|
||||
bool Erase(FEXCore::Core::CpuStateFrame* Frame, uint64_t Address, const LockToken&) {
|
||||
bool Erase(uint64_t Address, const LookupCacheWriteLockToken&) {
|
||||
// Sever any links to this block
|
||||
auto lower = BlockLinks->lower_bound({Address, nullptr});
|
||||
auto upper = BlockLinks->upper_bound({Address, reinterpret_cast<FEXCore::Context::ExitFunctionLinkData*>(UINTPTR_MAX)});
|
||||
for (auto it = lower; it != upper; it = BlockLinks->erase(it)) {
|
||||
it->second(Frame, it->first.HostLink);
|
||||
it->second(it->first.HostLink);
|
||||
}
|
||||
|
||||
// Remove from BlockList
|
||||
return BlockList.erase(Address) != 0;
|
||||
}
|
||||
|
||||
void InvalidateRange(uint64_t Start, uint64_t Length) {
|
||||
auto lk = AcquireWriteLock();
|
||||
|
||||
auto lower = CodePages.lower_bound(Start >> 12);
|
||||
auto upper = CodePages.upper_bound((Start + Length - 1) >> 12);
|
||||
|
||||
for (auto it = lower; it != upper; it++) {
|
||||
for (const auto& Entry : it->second) {
|
||||
Erase(Entry, lk);
|
||||
}
|
||||
}
|
||||
CodePages.erase(lower, upper);
|
||||
}
|
||||
|
||||
void AddBlockLink(uint64_t GuestDestination, FEXCore::Context::ExitFunctionLinkData* HostLink,
|
||||
const FEXCore::Context::BlockDelinkerFunc& delinker, const LockToken&) {
|
||||
const FEXCore::Context::BlockDelinkerFunc& delinker, const LookupCacheWriteLockToken&) {
|
||||
BlockLinks->insert({{GuestDestination, HostLink}, delinker});
|
||||
}
|
||||
|
||||
bool AddBlockExecutableRange(const fextl::set<uint64_t>& Addresses, uint64_t Start, uint64_t Length, const LockToken&) {
|
||||
bool AddBlockExecutableRange(const fextl::set<uint64_t>& Addresses, uint64_t Start, uint64_t Length, const LookupCacheWriteLockToken&) {
|
||||
bool rv = false;
|
||||
|
||||
for (auto CurrentPage = Start >> 12, EndPage = (Start + Length - 1) >> 12; CurrentPage <= EndPage; CurrentPage++) {
|
||||
@@ -107,7 +132,7 @@ struct GuestToHostMap {
|
||||
return rv;
|
||||
}
|
||||
|
||||
void ClearCache(const LockToken&);
|
||||
void ClearCache(const LookupCacheWriteLockToken&);
|
||||
};
|
||||
|
||||
class LookupCache {
|
||||
@@ -122,122 +147,198 @@ public:
|
||||
|
||||
// Swaps out the underlying GuestToHostMap and clears all associated caches.
|
||||
// This interface requires the previous CodeBuffer to be provided despite not using it. This ensures the shared write lock is still valid.
|
||||
void ChangeGuestToHostMapping([[maybe_unused]] CPU::CodeBuffer& Prev, GuestToHostMap& NewMap) {
|
||||
ClearThreadLocalCaches();
|
||||
void ChangeGuestToHostMapping([[maybe_unused]] CPU::CodeBuffer& Prev, GuestToHostMap& NewMap, const LookupCacheWriteLockToken& lk) {
|
||||
ClearThreadLocalCaches(lk);
|
||||
Shared = &NewMap;
|
||||
}
|
||||
|
||||
uintptr_t FindBlock(uint64_t Address) {
|
||||
uintptr_t FindBlock(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) {
|
||||
// Try L1, no lock needed
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1PointerMask];
|
||||
if (L1Entry.GuestCode == Address) {
|
||||
return L1Entry.HostCode;
|
||||
}
|
||||
|
||||
// L2 and L3 need to be locked
|
||||
auto lk = Shared->AcquireLock();
|
||||
uintptr_t HostPtr {};
|
||||
{
|
||||
std::optional<FEXCore::SHMStats::AccumulationBlock<uint64_t>> LockTime(
|
||||
Thread->ThreadStats ? &Thread->ThreadStats->AccumulatedCacheReadLockTime : nullptr);
|
||||
auto lk = Shared->AcquireWriteLock();
|
||||
LockTime.reset();
|
||||
|
||||
// Try L2
|
||||
const auto PageIndex = (Address & (VirtualMemSize - 1)) >> 12;
|
||||
const auto PageOffset = Address & (0x0FFF);
|
||||
if (!DisableL2Cache()) {
|
||||
// Try L2
|
||||
const auto PageIndex = (Address & (VirtualMemSize - 1)) >> 12;
|
||||
const auto PageOffset = Address & (0x0FFF);
|
||||
|
||||
const auto Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
auto LocalPagePointer = Pointers[PageIndex];
|
||||
const auto Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
auto LocalPagePointer = Pointers[PageIndex];
|
||||
|
||||
// Do we a page pointer for this address?
|
||||
if (LocalPagePointer) {
|
||||
// Find there pointer for the address in the blocks
|
||||
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
|
||||
// Do we a page pointer for this address?
|
||||
if (LocalPagePointer) {
|
||||
// Find there pointer for the address in the blocks
|
||||
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
|
||||
|
||||
if (BlockPointers[PageOffset].GuestCode == Address) {
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = BlockPointers[PageOffset].HostCode;
|
||||
return L1Entry.HostCode;
|
||||
if (BlockPointers[PageOffset].GuestCode == Address) {
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = BlockPointers[PageOffset].HostCode;
|
||||
HostPtr = L1Entry.HostCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!HostPtr) {
|
||||
// Try L3
|
||||
auto Entry = Shared->FindBlock(Address, lk);
|
||||
if (Entry) {
|
||||
CacheBlockMapping(Address, *Entry, false, lk);
|
||||
HostPtr = Entry->HostCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try L3
|
||||
auto HostCode = Shared->FindBlock(Address, lk);
|
||||
if (HostCode) {
|
||||
CacheBlockMapping(Address, HostCode.value());
|
||||
return HostCode.value();
|
||||
if (HostPtr && DynamicL1Cache()) {
|
||||
UpdateDynamicL1Stats(Thread);
|
||||
}
|
||||
|
||||
// Failed to find
|
||||
return 0;
|
||||
FEXCORE_PROFILE_INSTANT_INCREMENT(Thread, AccumulatedCacheMissCount, 1);
|
||||
|
||||
return HostPtr;
|
||||
}
|
||||
|
||||
void UpdateDynamicL1Stats(FEXCore::Core::InternalThreadState* Thread) {
|
||||
// If host pointer was found in L2 or L3, then add it to the counter.
|
||||
// Keeping track not L1 misses, but specifically L2/L3 hits.
|
||||
++L2L3CacheHits;
|
||||
|
||||
const auto CurrentTime = std::chrono::system_clock::now();
|
||||
const auto Period = CurrentTime - LastPeriod;
|
||||
if (Period >= SamplePeriod) {
|
||||
// If larger than the sample period then check if we need to increase L1 cache size.
|
||||
const double AveragePerSecond = static_cast<double>(L2L3CacheHits) /
|
||||
static_cast<double>(std::chrono::duration_cast<std::chrono::milliseconds>(Period).count()) * 1000.0;
|
||||
|
||||
if (AveragePerSecond >= DynamicL1CacheIncreaseCountHeuristic()) {
|
||||
if (CurrentL1Entries < MAX_L1_ENTRIES) {
|
||||
CurrentL1Entries <<= 1;
|
||||
L1PointerMask = CurrentL1Entries - 1;
|
||||
|
||||
// Update the thread's L1 pointer mask to increase how much cache it uses.
|
||||
// Since we're in C-code, this is safe to update here.
|
||||
Thread->CurrentFrame->State.L1Mask = GetScaledL1PointerMask();
|
||||
}
|
||||
} else if (AveragePerSecond < DynamicL1CacheDecreaseCountHeuristic()) {
|
||||
if (CurrentL1Entries > MIN_L1_ENTRIES) {
|
||||
CurrentL1Entries >>= 1;
|
||||
L1PointerMask = CurrentL1Entries - 1;
|
||||
|
||||
// Madvise the entries that we are dropping. Gives the memory back to the OS.
|
||||
LookupCacheEntry* FirstZeroL1Entry = &reinterpret_cast<LookupCacheEntry*>(L1Pointer)[CurrentL1Entries];
|
||||
size_t ZeroMemorySize = (MAX_L1_ENTRIES - CurrentL1Entries) * sizeof(LookupCacheEntry);
|
||||
FEXCore::Allocator::VirtualDontNeed(FirstZeroL1Entry, ZeroMemorySize, false);
|
||||
|
||||
// Update the thread's L1 pointer mask to increase how much cache it uses.
|
||||
// Since we're in C-code, this is safe to update here.
|
||||
Thread->CurrentFrame->State.L1Mask = GetScaledL1PointerMask();
|
||||
}
|
||||
}
|
||||
|
||||
// Update Last period to start again.
|
||||
LastPeriod = CurrentTime;
|
||||
L2L3CacheHits = 0;
|
||||
}
|
||||
}
|
||||
|
||||
GuestToHostMap* Shared = nullptr;
|
||||
|
||||
// Appends a list of Block {Address} to CodePages [Start, Start + Length)
|
||||
// Returns true if new pages are marked as containing code
|
||||
bool AddBlockExecutableRange(const fextl::set<uint64_t>& Addresses, uint64_t Start, uint64_t Length) {
|
||||
auto lk = Shared->AcquireLock();
|
||||
bool AddBlockExecutableRange(FEXCore::Core::InternalThreadState* Thread, const fextl::set<uint64_t>& Addresses, uint64_t Start, uint64_t Length) {
|
||||
std::optional<FEXCore::SHMStats::AccumulationBlock<uint64_t>> LockTime(
|
||||
Thread->ThreadStats ? &Thread->ThreadStats->AccumulatedCacheWriteLockTime : nullptr);
|
||||
auto lk = Shared->AcquireWriteLock();
|
||||
LockTime.reset();
|
||||
|
||||
return Shared->AddBlockExecutableRange(Addresses, Start, Length, lk);
|
||||
}
|
||||
|
||||
// Adds to Guest -> Host code mapping
|
||||
void AddBlockMapping(uint64_t Address, void* HostCode) {
|
||||
auto lk = Shared->AcquireLock();
|
||||
void AddBlockMapping(FEXCore::Core::InternalThreadState* Thread, uint64_t Address, const fextl::vector<uint64_t>& CodePages, void* HostCode) {
|
||||
std::optional<FEXCore::SHMStats::AccumulationBlock<uint64_t>> LockTime(
|
||||
Thread->ThreadStats ? &Thread->ThreadStats->AccumulatedCacheWriteLockTime : nullptr);
|
||||
auto lk = Shared->AcquireWriteLock();
|
||||
LockTime.reset();
|
||||
|
||||
Shared->AddBlockMapping(Address, HostCode, lk);
|
||||
const auto& Entry = Shared->AddBlockMapping(Address, CodePages, HostCode, lk);
|
||||
|
||||
// There is no need to update L1 or L2, they will get updated on first lookup
|
||||
// However, adding to L1 here increases performance
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = (uintptr_t)HostCode;
|
||||
CacheBlockMapping(Address, Entry, true, lk);
|
||||
}
|
||||
|
||||
// NOTE: It's the caller's responsibility to call Erase() for all other
|
||||
// GuestToHostMaps that share the same LookupCache. Otherwise, the
|
||||
// L1/L2 caches will contain stale references to deallocated memory.
|
||||
bool Erase(FEXCore::Core::CpuStateFrame* Frame, uint64_t Address) {
|
||||
auto lk = Shared->AcquireLock();
|
||||
|
||||
bool ErasedAny = Shared->Erase(Frame, Address, lk);
|
||||
|
||||
// Invalidates L1/L2 for a given guest block
|
||||
void InvalidateCache(uint64_t Address, const LookupCacheWriteLockToken& lk) {
|
||||
// Do L1
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1PointerMask];
|
||||
if (L1Entry.GuestCode == Address) {
|
||||
L1Entry.GuestCode = 0;
|
||||
ErasedAny = true;
|
||||
// Leave L1Entry.HostCode as is, so that concurrent lookups won't read a null pointer
|
||||
// This is a soft guarantee for cross thread invalidation, as atomics are not used
|
||||
// and it hasn't been thoroughly tested
|
||||
}
|
||||
|
||||
// Do full map
|
||||
Address = Address & (VirtualMemSize - 1);
|
||||
uint64_t PageOffset = Address & (0x0FFF);
|
||||
Address >>= 12;
|
||||
if (!DisableL2Cache()) {
|
||||
// Do full map
|
||||
Address = Address & (VirtualMemSize - 1);
|
||||
uint64_t PageOffset = Address & (0x0FFF);
|
||||
Address >>= 12;
|
||||
|
||||
uintptr_t* Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
uint64_t LocalPagePointer = Pointers[Address];
|
||||
if (!LocalPagePointer) {
|
||||
// Page for this code didn't even exist, nothing to do
|
||||
return ErasedAny;
|
||||
uintptr_t* Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
uint64_t LocalPagePointer = Pointers[Address];
|
||||
if (!LocalPagePointer) {
|
||||
// Page for this code didn't even exist, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
// Page exists, just set the offset to zero
|
||||
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
|
||||
BlockPointers[PageOffset].GuestCode = 0;
|
||||
BlockPointers[PageOffset].HostCode = 0;
|
||||
}
|
||||
|
||||
// Page exists, just set the offset to zero
|
||||
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
|
||||
BlockPointers[PageOffset].GuestCode = 0;
|
||||
BlockPointers[PageOffset].HostCode = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AddBlockLink(uint64_t GuestDestination, FEXCore::Context::ExitFunctionLinkData* HostLink, const FEXCore::Context::BlockDelinkerFunc& delinker) {
|
||||
auto lk = Shared->AcquireLock();
|
||||
// Invalidates all L1/L2 entries for all guest block that intersect the given range
|
||||
bool InvalidateCacheRange(uint64_t Start, uint64_t Length) {
|
||||
auto lk = Shared->AcquireWriteLock();
|
||||
|
||||
auto lower = CachedCodePages.lower_bound(Start >> 12);
|
||||
auto upper = CachedCodePages.upper_bound((Start + Length - 1) >> 12);
|
||||
|
||||
for (auto it = lower; it != upper; it++) {
|
||||
for (const auto& Entry : it->second) {
|
||||
InvalidateCache(Entry, lk);
|
||||
}
|
||||
}
|
||||
CachedCodePages.erase(lower, upper);
|
||||
return upper != lower;
|
||||
}
|
||||
|
||||
void AddBlockLink(uint64_t GuestDestination, FEXCore::Context::ExitFunctionLinkData* HostLink,
|
||||
const FEXCore::Context::BlockDelinkerFunc& delinker, const LookupCacheWriteLockToken& lk) {
|
||||
Shared->AddBlockLink(GuestDestination, HostLink, delinker, lk);
|
||||
}
|
||||
|
||||
void ClearCache();
|
||||
void ClearL2Cache();
|
||||
void ClearThreadLocalCaches();
|
||||
void ClearCache(const LookupCacheWriteLockToken&);
|
||||
void ClearL2Cache(const LookupCacheWriteLockToken&);
|
||||
void ClearThreadLocalCaches(const LookupCacheWriteLockToken&);
|
||||
|
||||
uintptr_t GetL1Pointer() const {
|
||||
return L1Pointer;
|
||||
}
|
||||
uintptr_t GetScaledL1PointerMask() const {
|
||||
return L1PointerMask << FEXCore::ilog2(sizeof(LookupCache::LookupCacheEntry));
|
||||
}
|
||||
uintptr_t GetPagePointer() const {
|
||||
return PagePointer;
|
||||
}
|
||||
@@ -245,9 +346,6 @@ public:
|
||||
return VirtualMemSize;
|
||||
}
|
||||
|
||||
constexpr static size_t L1_ENTRIES = 1 * 1024 * 1024; // Must be a power of 2
|
||||
constexpr static size_t L1_ENTRIES_MASK = L1_ENTRIES - 1;
|
||||
|
||||
// This needs to be taken before reads or writes to L2, L3, CodePages,
|
||||
// and before writes to L1. Concurrent access from a thread that this LookupCache doesn't belong to
|
||||
// may only happen during cross thread invalidation (::Erase).
|
||||
@@ -255,45 +353,52 @@ public:
|
||||
// Some care is taken so that L1 lookups can be done without locks, and even tearing is unlikely to lead to a crash.
|
||||
// This approach has not been fully vetted yet.
|
||||
// Also note that L1 lookups might be inlined in the JIT Dispatcher and/or block ends.
|
||||
auto AcquireLock() {
|
||||
return Shared->AcquireLock();
|
||||
auto AcquireWriteLock() {
|
||||
return Shared->AcquireWriteLock();
|
||||
}
|
||||
|
||||
private:
|
||||
void CacheBlockMapping(uint64_t Address, uintptr_t HostCode) {
|
||||
// Do L1
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1_ENTRIES_MASK];
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = HostCode;
|
||||
|
||||
// Do ful map
|
||||
auto FullAddress = Address;
|
||||
Address = Address & (VirtualMemSize - 1);
|
||||
|
||||
uint64_t PageOffset = Address & (0x0FFF);
|
||||
Address >>= 12;
|
||||
uintptr_t* Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
uint64_t LocalPagePointer = Pointers[Address];
|
||||
if (!LocalPagePointer) {
|
||||
// We don't have a page pointer for this address
|
||||
// Allocate one now if we can
|
||||
uintptr_t NewPageBacking = AllocateBackingForPage();
|
||||
if (!NewPageBacking) {
|
||||
// Couldn't allocate, clear L2 and retry
|
||||
ClearL2Cache();
|
||||
CacheBlockMapping(Address, HostCode);
|
||||
return;
|
||||
}
|
||||
Pointers[Address] = NewPageBacking;
|
||||
LocalPagePointer = NewPageBacking;
|
||||
void CacheBlockMapping(uint64_t Address, const GuestToHostMap::BlockEntry& Entry, bool L1Only, const LookupCacheWriteLockToken& lk) {
|
||||
for (const auto& CodePage : Entry.CodePages) {
|
||||
CachedCodePages[CodePage >> 12].insert(Address);
|
||||
}
|
||||
|
||||
// Add the new pointer to the page block
|
||||
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
|
||||
// Do L1
|
||||
auto& L1Entry = reinterpret_cast<LookupCacheEntry*>(L1Pointer)[Address & L1PointerMask];
|
||||
L1Entry.GuestCode = Address;
|
||||
L1Entry.HostCode = Entry.HostCode;
|
||||
|
||||
// This silently replaces existing mappings
|
||||
BlockPointers[PageOffset].GuestCode = FullAddress;
|
||||
BlockPointers[PageOffset].HostCode = HostCode;
|
||||
if (!DisableL2Cache() && !L1Only) {
|
||||
// Do ful map
|
||||
auto FullAddress = Address;
|
||||
Address = Address & (VirtualMemSize - 1);
|
||||
|
||||
uint64_t PageOffset = Address & (0x0FFF);
|
||||
Address >>= 12;
|
||||
|
||||
uintptr_t* Pointers = reinterpret_cast<uintptr_t*>(PagePointer);
|
||||
uint64_t LocalPagePointer = Pointers[Address];
|
||||
if (!LocalPagePointer) {
|
||||
// We don't have a page pointer for this address
|
||||
// Allocate one now if we can
|
||||
uintptr_t NewPageBacking = AllocateBackingForPage();
|
||||
if (!NewPageBacking) {
|
||||
// Couldn't allocate, clear L2 and retry
|
||||
ClearL2Cache(lk);
|
||||
CacheBlockMapping(Address, Entry, false, lk);
|
||||
return;
|
||||
}
|
||||
Pointers[Address] = NewPageBacking;
|
||||
LocalPagePointer = NewPageBacking;
|
||||
}
|
||||
|
||||
// Add the new pointer to the page block
|
||||
auto BlockPointers = reinterpret_cast<LookupCacheEntry*>(LocalPagePointer);
|
||||
|
||||
// This silently replaces existing mappings
|
||||
BlockPointers[PageOffset].GuestCode = FullAddress;
|
||||
BlockPointers[PageOffset].HostCode = Entry.HostCode;
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t AllocateBackingForPage() {
|
||||
@@ -310,19 +415,38 @@ private:
|
||||
return PageMemory + NewBase;
|
||||
}
|
||||
|
||||
// Maps from a page index to all blocks in the page that have at some point been fetched into L1/L2
|
||||
fextl::map<uint64_t, fextl::unordered_set<uint64_t>> CachedCodePages;
|
||||
|
||||
uintptr_t PagePointer;
|
||||
uintptr_t PageMemory;
|
||||
uintptr_t L1Pointer;
|
||||
uintptr_t L1PointerMask;
|
||||
|
||||
size_t TotalCacheSize;
|
||||
|
||||
// Start with 8k entries in L1 to give 128KB of L1 cache to each thread.
|
||||
// Max out at 1 million entries to give each thread 16MB of L1 cache maximum.
|
||||
constexpr static size_t MIN_L1_ENTRIES = 8 * 1024; // Must be a power of 2
|
||||
constexpr static size_t MAX_L1_ENTRIES = 1 * 1024 * 1024; // Must be a power of 2
|
||||
|
||||
constexpr static size_t CODE_SIZE = 128 * 1024 * 1024;
|
||||
constexpr static size_t SIZE_PER_PAGE = 4096 * sizeof(LookupCacheEntry);
|
||||
constexpr static size_t L1_SIZE = L1_ENTRIES * sizeof(LookupCacheEntry);
|
||||
constexpr static size_t SIZE_PER_PAGE = FEXCore::Utils::FEX_PAGE_SIZE * sizeof(LookupCacheEntry);
|
||||
constexpr static size_t MAX_L1_SIZE = MAX_L1_ENTRIES * sizeof(LookupCacheEntry);
|
||||
|
||||
size_t AllocateOffset {};
|
||||
|
||||
FEXCore::Context::ContextImpl* ctx;
|
||||
uint64_t VirtualMemSize {};
|
||||
|
||||
size_t CurrentL1Entries = MIN_L1_ENTRIES;
|
||||
uint64_t L2L3CacheHits {};
|
||||
std::chrono::time_point<std::chrono::system_clock> LastPeriod {};
|
||||
constexpr static std::chrono::seconds SamplePeriod {1};
|
||||
FEX_CONFIG_OPT(DynamicL1CacheIncreaseCountHeuristic, DYNAMICL1CACHEINCREASECOUNTHEURISTIC);
|
||||
FEX_CONFIG_OPT(DynamicL1CacheDecreaseCountHeuristic, DYNAMICL1CACHEDECREASECOUNTHEURISTIC);
|
||||
|
||||
FEX_CONFIG_OPT(DynamicL1Cache, DYNAMICL1CACHE);
|
||||
FEX_CONFIG_OPT(DisableL2Cache, DISABLEL2CACHE);
|
||||
};
|
||||
} // namespace FEXCore
|
||||
@@ -28,7 +28,6 @@ $end_info$
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <tuple>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
@@ -51,8 +50,6 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) {
|
||||
FEXCore::X86State::REG_RSI, FEXCore::X86State::REG_RDI, FEXCore::X86State::REG_RBP,
|
||||
};
|
||||
|
||||
SyscallFlags DefaultSyscallFlags = FEXCore::IR::SyscallFlags::DEFAULT;
|
||||
|
||||
const auto OSABI = CTX->SyscallHandler->GetOSABI();
|
||||
if (OSABI == FEXCore::HLE::SyscallOSABI::OS_LINUX64) {
|
||||
NumArguments = GPRIndexes_64.size();
|
||||
@@ -64,7 +61,6 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) {
|
||||
// All registers will be spilled before the syscall and filled afterwards so no JIT-side argument handling is necessary.
|
||||
NumArguments = 0;
|
||||
GPRIndexes = nullptr;
|
||||
DefaultSyscallFlags = FEXCore::IR::SyscallFlags::NORETURNEDRESULT;
|
||||
} else {
|
||||
ERROR_AND_DIE_FMT("Unhandled OSABI syscall");
|
||||
}
|
||||
@@ -98,9 +94,10 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs, bool IsSyscallInst) {
|
||||
}
|
||||
|
||||
FlushRegisterCache();
|
||||
auto SyscallOp = _Syscall(Arguments[0], Arguments[1], Arguments[2], Arguments[3], Arguments[4], Arguments[5], Arguments[6], DefaultSyscallFlags);
|
||||
auto SyscallOp = _Syscall(Arguments[0], Arguments[1], Arguments[2], Arguments[3], Arguments[4], Arguments[5], Arguments[6]);
|
||||
|
||||
if ((DefaultSyscallFlags & FEXCore::IR::SyscallFlags::NORETURNEDRESULT) != FEXCore::IR::SyscallFlags::NORETURNEDRESULT) {
|
||||
// Generic ABI doesn't store result in RAX.
|
||||
if (OSABI != FEXCore::HLE::SyscallOSABI::OS_GENERIC) {
|
||||
StoreGPRRegister(X86State::REG_RAX, SyscallOp);
|
||||
}
|
||||
|
||||
@@ -153,12 +150,6 @@ void OpDispatchBuilder::NOPOp(OpcodeArgs) {}
|
||||
void OpDispatchBuilder::RETOp(OpcodeArgs) {
|
||||
const auto GPRSize = GetGPROpSize();
|
||||
|
||||
// ABI Optimization: Flags don't survive calls or rets
|
||||
if (CTX->Config.ABILocalFlags) {
|
||||
_InvalidateFlags(~0UL); // all flags
|
||||
InvalidatePF_AF();
|
||||
}
|
||||
|
||||
Ref SP = _RMWHandle(LoadGPRRegister(X86State::REG_RSP));
|
||||
Ref NewRIP = Pop(GPRSize, SP);
|
||||
|
||||
@@ -522,12 +513,6 @@ void OpDispatchBuilder::CALLOp(OpcodeArgs) {
|
||||
|
||||
BlockSetRIP = true;
|
||||
|
||||
// ABI Optimization: Flags don't survive calls or rets
|
||||
if (CTX->Config.ABILocalFlags) {
|
||||
_InvalidateFlags(~0UL); // all flags
|
||||
InvalidatePF_AF();
|
||||
}
|
||||
|
||||
// Call instruction only uses up to 32-bit signed displacement
|
||||
int64_t TargetOffset = Op->Src[0].Literal();
|
||||
|
||||
@@ -3230,7 +3215,11 @@ void OpDispatchBuilder::STOSOp(OpcodeArgs) {
|
||||
Ref Dest = MakeSegmentAddress(X86State::REG_RDI, 0, X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
|
||||
|
||||
// Store to memory where RDI points
|
||||
_StoreMemGPRAutoTSO(Size, Dest, Src, Size);
|
||||
if (CTX->IsMemcpyAtomicTSOEnabled()) {
|
||||
_StoreMemGPRAutoTSO(Size, Dest, Src, Size);
|
||||
} else {
|
||||
_StoreMem(RegClass::GPR, Size, Src, Dest, Invalid(), OpSize::i8Bit, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
|
||||
// Offset the pointer
|
||||
Ref TailDest = LoadGPRRegister(X86State::REG_RDI);
|
||||
@@ -3298,10 +3287,15 @@ void OpDispatchBuilder::MOVSOp(OpcodeArgs) {
|
||||
Ref RSI = MakeSegmentAddress(X86State::REG_RSI, Op->Flags, X86Tables::DecodeFlags::FLAG_DS_PREFIX);
|
||||
Ref RDI = MakeSegmentAddress(X86State::REG_RDI, 0, X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
|
||||
|
||||
auto Src = _LoadMemGPRAutoTSO(Size, RSI, Size);
|
||||
if (CTX->IsMemcpyAtomicTSOEnabled()) {
|
||||
auto Src = _LoadMemGPRAutoTSO(Size, RSI, Size);
|
||||
|
||||
// Store to memory where RDI points
|
||||
_StoreMemGPRAutoTSO(Size, RDI, Src, Size);
|
||||
// Store to memory where RDI points
|
||||
_StoreMemGPRAutoTSO(Size, RDI, Src, Size);
|
||||
} else {
|
||||
auto Src = _LoadMem(RegClass::GPR, Size, RSI, Invalid(), OpSize::i8Bit, MemOffsetType::SXTX, 1);
|
||||
_StoreMem(RegClass::GPR, Size, Src, RDI, Invalid(), OpSize::i8Bit, MemOffsetType::SXTX, 1);
|
||||
}
|
||||
|
||||
auto PtrDir = LoadDir(IR::OpSizeToSize(Size));
|
||||
RSI = Add(OpSize::i64Bit, RSI, PtrDir);
|
||||
|
||||
@@ -1330,7 +1330,6 @@ protected:
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ReducedPrecisionMode, X87REDUCEDPRECISION);
|
||||
FEX_CONFIG_OPT(StrictReducedPrecisionMode, X87STRICTREDUCEDPRECISION);
|
||||
|
||||
struct JumpTargetInfo {
|
||||
Ref BlockEntry;
|
||||
@@ -2528,7 +2527,7 @@ private:
|
||||
const bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
|
||||
|
||||
// Use ldp if possible, otherwise fallback on two loads.
|
||||
if (!AtomicTSO && !A.Segment && Size >= OpSize::i32Bit & Size <= OpSize::i128Bit) {
|
||||
if (!AtomicTSO && !A.Segment && Size >= OpSize::i32Bit && Size <= OpSize::i128Bit) {
|
||||
const auto B = SelectPairAddressMode(A, Size);
|
||||
return LoadMemPair(Class, Size, B.Base, B.Offset);
|
||||
}
|
||||
@@ -2567,7 +2566,7 @@ private:
|
||||
const bool AtomicTSO = IsTSOEnabled(Class) && !A.NonTSO;
|
||||
|
||||
// Use stp if possible, otherwise fallback on two stores.
|
||||
if (!AtomicTSO && !A.Segment && Size >= OpSize::i32Bit & Size <= OpSize::i128Bit) {
|
||||
if (!AtomicTSO && !A.Segment && Size >= OpSize::i32Bit && Size <= OpSize::i128Bit) {
|
||||
const auto B = SelectPairAddressMode(A, Size);
|
||||
_StoreMemPair(Class, Size, Value1, Value2, B.Base, B.Offset);
|
||||
} else {
|
||||
|
||||
@@ -1956,7 +1956,7 @@ void OpDispatchBuilder::AVX128_VFMAImpl(OpcodeArgs, IROps IROp, uint8_t Src1Idx,
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::AVX128_VFMAScalarImpl(OpcodeArgs, IROps IROp, uint8_t Src1Idx, uint8_t Src2Idx, uint8_t AddendIdx) {
|
||||
const auto SrcSize = OpSizeFromSrc(Op);
|
||||
const OpSize ElementSize = Op->Flags & X86Tables::DecodeFlags::FLAG_OPTION_AVX_W ? OpSize::i64Bit : OpSize::i32Bit;
|
||||
|
||||
auto Dest = AVX128_LoadSource_WithOpSize(Op, Op->Dest, Op->Flags, false).Low;
|
||||
auto Src1 = AVX128_LoadSource_WithOpSize(Op, Op->Src[0], Op->Flags, false).Low;
|
||||
@@ -1964,13 +1964,13 @@ void OpDispatchBuilder::AVX128_VFMAScalarImpl(OpcodeArgs, IROps IROp, uint8_t Sr
|
||||
if (Op->Src[1].IsGPR()) {
|
||||
Src2 = AVX128_LoadSource_WithOpSize(Op, Op->Src[1], Op->Flags, false).Low;
|
||||
} else {
|
||||
Src2 = LoadSourceFPR_WithOpSize(Op, Op->Src[1], SrcSize, Op->Flags);
|
||||
Src2 = LoadSourceFPR_WithOpSize(Op, Op->Src[1], ElementSize, Op->Flags);
|
||||
}
|
||||
|
||||
Ref Sources[3] = {Dest, Src1, Src2};
|
||||
|
||||
DeriveOp(Result_Low, IROp,
|
||||
_VFMLAScalarInsert(OpSize::i128Bit, SrcSize, Dest, Sources[Src1Idx - 1], Sources[Src2Idx - 1], Sources[AddendIdx - 1]));
|
||||
_VFMLAScalarInsert(OpSize::i128Bit, ElementSize, Dest, Sources[Src1Idx - 1], Sources[Src2Idx - 1], Sources[AddendIdx - 1]));
|
||||
AVX128_StoreResult_WithOpSize(Op, Op->Dest, AVX128_Zext(Result_Low));
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ constexpr DispatchTableEntry OpDispatch_TwoByteOpTable[] = {
|
||||
|
||||
#ifndef _WIN32
|
||||
// FEX reserved instructions
|
||||
{0x37, 1, &OpDispatchBuilder::CallbackReturnOp},
|
||||
{0x3E, 1, &OpDispatchBuilder::CallbackReturnOp},
|
||||
{0x3F, 1, &OpDispatchBuilder::ThunkOp},
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ X86GeneratedCode::X86GeneratedCode() {
|
||||
CodePtr = AllocateGuestCodeSpace(CODE_SIZE);
|
||||
|
||||
constexpr std::array<uint8_t, 2> SignalReturnCode = {
|
||||
0x0F, 0x37, // CALLBACKRET FEX Instruction
|
||||
0x0F, 0x3E, // CALLBACKRET FEX Instruction
|
||||
};
|
||||
|
||||
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr);
|
||||
@@ -51,7 +51,9 @@ void* X86GeneratedCode::AllocateGuestCodeSpace(size_t Size) {
|
||||
|
||||
if (Is64BitMode()) {
|
||||
// 64bit mode can have its sigret handler anywhere
|
||||
return FEXCore::Allocator::VirtualAlloc(Size);
|
||||
auto Result = FEXCore::Allocator::VirtualAlloc(Size);
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(Result), Size);
|
||||
return Result;
|
||||
}
|
||||
|
||||
// First 64bit page
|
||||
|
||||
@@ -100,10 +100,11 @@ constexpr std::array<X86InstInfo, MAX_SECOND_TABLE_SIZE> SecondBaseOps = []() co
|
||||
{0x34, 1, X86InstInfo{"SYSENTER", TYPE_INST, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x35, 1, X86InstInfo{"SYSEXIT", TYPE_INST, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x36, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x37, 1, X86InstInfo{"GETSEC", TYPE_INVALID, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x38, 1, X86InstInfo{"", TYPE_0F38_TABLE, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x39, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x3A, 1, X86InstInfo{"", TYPE_0F3A_TABLE, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x3B, 4, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0}},
|
||||
{0x3B, 3, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0}},
|
||||
|
||||
{0x40, 1, X86InstInfo{"CMOVO", TYPE_INST, FLAGS_MODRM | FLAGS_NO_OVERLAY, 0}},
|
||||
{0x41, 1, X86InstInfo{"CMOVNO", TYPE_INST, FLAGS_MODRM | FLAGS_NO_OVERLAY, 0}},
|
||||
@@ -299,7 +300,7 @@ constexpr std::array<X86InstInfo, MAX_SECOND_TABLE_SIZE> SecondBaseOps = []() co
|
||||
// FEX reserved instructions
|
||||
// Unused x86 encoding instruction.
|
||||
|
||||
{0x37, 1, X86InstInfo{"CALLBACKRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0}},
|
||||
{0x3E, 1, X86InstInfo{"CALLBACKRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0}},
|
||||
|
||||
// This was originally used by VIA to jump to its alternative instruction set. Used for OP_THUNK
|
||||
{0x3F, 1, X86InstInfo{"ALTINST", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0}},
|
||||
|
||||
@@ -138,7 +138,6 @@
|
||||
"FenceType": "FenceType",
|
||||
"RegisterClass": "RegClass",
|
||||
"CondClass": "CondClass",
|
||||
"SyscallFlags": "FEXCore::IR::SyscallFlags",
|
||||
"SHA256Sum": "SHA256Sum",
|
||||
"MemOffsetType": "MemOffsetType",
|
||||
"BreakDefinition": "BreakDefinition",
|
||||
@@ -314,25 +313,13 @@
|
||||
"CallbackReturn": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"GPR = Syscall GPR:$SyscallID, GPR:$Arg0, GPR:$Arg1, GPR:$Arg2, GPR:$Arg3, GPR:$Arg4, GPR:$Arg5, SyscallFlags:$Flags": {
|
||||
"GPR = Syscall GPR:$SyscallID, GPR:$Arg0, GPR:$Arg1, GPR:$Arg2, GPR:$Arg3, GPR:$Arg4, GPR:$Arg5": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Dispatches a guest syscall through to the SyscallHandler class"
|
||||
],
|
||||
"DestSize": "OpSize::i64Bit"
|
||||
},
|
||||
|
||||
"GPR = InlineSyscall GPR:$Arg0, GPR:$Arg1, GPR:$Arg2, GPR:$Arg3, GPR:$Arg4, GPR:$Arg5, i32:$HostSyscallNumber, SyscallFlags:$Flags": {
|
||||
"HasSideEffects": true,
|
||||
"Desc": ["Dispatches a guest syscall directly to the host syscall interface,",
|
||||
"bypassing the SyscallHandler class used by Syscall.",
|
||||
"This has significantly less overhead than Syscall, which needs to save JIT state first.",
|
||||
"Can only be used for syscalls that match across architecture,",
|
||||
"such as gettid (matches on x86/x86-64/Arm64)."
|
||||
],
|
||||
|
||||
"DestSize": "OpSize::i64Bit"
|
||||
},
|
||||
|
||||
"Thunk GPR:$ArgPtr, SHA256Sum:$ThunkNameHash": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
@@ -578,8 +565,7 @@
|
||||
"Desc": ["Does a x86 TSO compatible load from memory. Offset must be Invalid()."
|
||||
],
|
||||
"Inline": ["", "Memtso"],
|
||||
"DestSize": "Size",
|
||||
"DynamicDispatch": true
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"StoreMemTSO RegisterClass:$Class, OpSize:#Size, SSA:$Value, GPR:$Addr, GPR:$Offset, OpSize:$Align, MemOffsetType:$OffsetType, u8:$OffsetScale": {
|
||||
@@ -587,8 +573,7 @@
|
||||
],
|
||||
"Inline": ["Zero", "", "Memtso"],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "Size",
|
||||
"DynamicDispatch": true
|
||||
"DestSize": "Size"
|
||||
},
|
||||
|
||||
"FPR = VLoadVectorMasked OpSize:#RegisterSize, OpSize:#ElementSize, FPR:$Mask, GPR:$Addr, GPR:$Offset, MemOffsetType:$OffsetType, u8:$OffsetScale": {
|
||||
|
||||
@@ -149,20 +149,6 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, RoundMode Arg)
|
||||
}();
|
||||
}
|
||||
|
||||
static void PrintArg(fextl::stringstream* out, const IRListView*, SyscallFlags Arg) {
|
||||
*out << [Arg] {
|
||||
switch (Arg) {
|
||||
case SyscallFlags::DEFAULT: return "Default";
|
||||
case SyscallFlags::OPTIMIZETHROUGH: return "Optimize Through";
|
||||
case SyscallFlags::NOSYNCSTATEONENTRY: return "No Sync State on Entry";
|
||||
case SyscallFlags::NORETURN: return "No Return";
|
||||
case SyscallFlags::NOSIDEEFFECTS: return "No Side Effects";
|
||||
case SyscallFlags::NORETURNEDRESULT: return "No Returned Result";
|
||||
}
|
||||
return "<Unknown Syscall Flags>";
|
||||
}();
|
||||
}
|
||||
|
||||
static void PrintArg(fextl::stringstream* out, const IRListView*, NamedVectorConstant Arg) {
|
||||
*out << [Arg] {
|
||||
// clang-format off
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
PassManager* Manager;
|
||||
PassManager* Manager {};
|
||||
};
|
||||
|
||||
class PassManager final {
|
||||
|
||||
@@ -63,9 +63,9 @@ public:
|
||||
private:
|
||||
RegisterClassData Classes[IR::NumClasses];
|
||||
|
||||
IREmitter* IREmit;
|
||||
IRListView* IR;
|
||||
const FEXCore::CPUIDEmu* CPUID;
|
||||
IREmitter* IREmit {};
|
||||
IRListView* IR {};
|
||||
const FEXCore::CPUIDEmu* CPUID {};
|
||||
|
||||
// Map of nodes to their preferred register, to coalesce load/store reg.
|
||||
fextl::vector<PhysicalRegister> PreferredReg;
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
fextl::vector<bool> Seen;
|
||||
|
||||
// SourcesNextUses is read backwards, this tracks the index
|
||||
int64_t SourceIndex;
|
||||
int64_t SourceIndex {};
|
||||
|
||||
bool Rematerializable(IROp_Header* IROp) {
|
||||
return IROp->Op == OP_CONSTANT;
|
||||
@@ -110,7 +110,7 @@ private:
|
||||
// block, so we don't need to size the block up-front.
|
||||
fextl::vector<uint32_t> NextUses;
|
||||
|
||||
bool AnySpilled;
|
||||
bool AnySpilled {};
|
||||
|
||||
bool IsValidArg(OrderedNodeWrapper Arg) {
|
||||
if (Arg.IsInvalid()) {
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
virtual void AddRegisters(RegClass Class, uint32_t RegisterCount) = 0;
|
||||
|
||||
// Number of GPRs usable for pairs at start of GPR set. Must be even.
|
||||
uint32_t PairRegs;
|
||||
uint32_t PairRegs {};
|
||||
};
|
||||
|
||||
} // namespace FEXCore::IR
|
||||
@@ -158,9 +158,7 @@ public:
|
||||
: Features(Features)
|
||||
, GPROpSize(GPROpSize) {
|
||||
FEX_CONFIG_OPT(ReducedPrecision, X87REDUCEDPRECISION);
|
||||
FEX_CONFIG_OPT(StrictReducedPrecision, X87STRICTREDUCEDPRECISION);
|
||||
ReducedPrecisionMode = ReducedPrecision;
|
||||
StrictReducedPrecisionMode = StrictReducedPrecision;
|
||||
}
|
||||
void Run(IREmitter* Emit) override;
|
||||
|
||||
@@ -168,12 +166,10 @@ private:
|
||||
const FEXCore::HostFeatures& Features;
|
||||
const OpSize GPROpSize;
|
||||
bool ReducedPrecisionMode;
|
||||
bool StrictReducedPrecisionMode;
|
||||
FEX_CONFIG_OPT(DisableVixlIndirectCalls, DISABLE_VIXL_INDIRECT_RUNTIME_CALLS);
|
||||
|
||||
// Helpers
|
||||
Ref RotateRight8(uint32_t V, Ref Amount);
|
||||
Ref SilenceNaN(Ref Value);
|
||||
|
||||
void F80SplitStore_Helper(const IROp_StoreStackMem* Op, Ref StackNode) {
|
||||
Ref AddrNode = IR->GetNode(Op->Addr);
|
||||
@@ -208,9 +204,6 @@ private:
|
||||
case OpSize::i32Bit:
|
||||
case OpSize::i64Bit: {
|
||||
StackNode = IREmit->_F80CVT(Op->StoreSize, StackNode);
|
||||
if (!ReducedPrecisionMode || StrictReducedPrecisionMode) {
|
||||
StackNode = SilenceNaN(StackNode);
|
||||
}
|
||||
IREmit->_StoreMemFPR(Op->StoreSize, StackNode, AddrNode, Offset, Align, OffsetType, OffsetScale);
|
||||
break;
|
||||
}
|
||||
@@ -242,10 +235,6 @@ private:
|
||||
MemOffsetType OffsetType = Op->OffsetType;
|
||||
uint8_t OffsetScale = Op->OffsetScale;
|
||||
|
||||
if ((!ReducedPrecisionMode || StrictReducedPrecisionMode) && Op->StoreSize != OpSize::f80Bit) {
|
||||
StackNode = SilenceNaN(StackNode);
|
||||
}
|
||||
|
||||
switch (Op->StoreSize) {
|
||||
case OpSize::i32Bit: {
|
||||
StackNode = IREmit->_Float_FToF(OpSize::i32Bit, OpSize::i64Bit, StackNode);
|
||||
@@ -499,15 +488,6 @@ inline Ref X87StackOptimization::RotateRight8(uint32_t V, Ref Amount) {
|
||||
return IREmit->_Lshr(OpSize::i32Bit, GetConstant(V | (V << 8)), Amount);
|
||||
}
|
||||
|
||||
inline Ref X87StackOptimization::SilenceNaN(Ref Value) {
|
||||
Ref GPRValue = IREmit->_VExtractToGPR(OpSize::i64Bit, OpSize::i64Bit, Value, 0);
|
||||
|
||||
IREmit->_FCmp(OpSize::i64Bit, Value, Value); // Comparison with itself should set VS if nan
|
||||
Ref QuietNaNGPR = IREmit->_Or(OpSize::i64Bit, GPRValue, IREmit->_Constant(0x0008000000000000ULL));
|
||||
Ref SilencedValue = IREmit->_VCastFromGPR(OpSize::i64Bit, OpSize::i64Bit, QuietNaNGPR);
|
||||
return IREmit->_NZCVSelectV(OpSize::i64Bit, CondClass::VS, SilencedValue, Value);
|
||||
}
|
||||
|
||||
inline std::optional<X87StackOptimization::StackMemberInfo> X87StackOptimization::MigrateToSlowPath_IfInvalid(uint8_t Offset) {
|
||||
const auto& [Valid, StackMember] = StackData.top(Offset);
|
||||
MigrateToSlowPathIf(Valid != StackSlot::VALID);
|
||||
|
||||
@@ -53,10 +53,15 @@ void* FEX_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t off
|
||||
}
|
||||
|
||||
if (flags & MAP_ANONYMOUS) {
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Result, length, "FEXMem");
|
||||
VirtualName("FEXMem", Result, length);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void VirtualName(const char* Name, void* Ptr, size_t Size) {
|
||||
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Ptr, Size, Name);
|
||||
}
|
||||
|
||||
int FEX_munmap(void* addr, size_t length) {
|
||||
int Result = Alloc64->Munmap(addr, length);
|
||||
|
||||
@@ -88,7 +93,7 @@ void* DisableSBRKAllocations() {
|
||||
// calls won't allocate any memory through that.
|
||||
void* AlignedBRK = reinterpret_cast<void*>(FEXCore::AlignUp(reinterpret_cast<uintptr_t>(StartingSBRK), FEXCore::Utils::FEX_PAGE_SIZE));
|
||||
void* AfterBRK =
|
||||
mmap(AlignedBRK, FEXCore::Utils::FEX_PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE | MAP_NORESERVE, -1, 0);
|
||||
::mmap(AlignedBRK, FEXCore::Utils::FEX_PAGE_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE | MAP_NORESERVE, -1, 0);
|
||||
if (AfterBRK == INVALID_PTR) {
|
||||
// Couldn't allocate the page after the aligned brk? This should never happen.
|
||||
// FEXCore::LogMan isn't configured yet so we just need to print the message.
|
||||
@@ -289,7 +294,7 @@ fextl::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
|
||||
--StackRegionIt;
|
||||
|
||||
auto Alloc =
|
||||
mmap(StackRegionIt->Ptr, StackRegionIt->Size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
::mmap(StackRegionIt->Ptr, StackRegionIt->Size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
|
||||
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({},{:x}) failed", fmt::ptr(StackRegionIt->Ptr), StackRegionIt->Size);
|
||||
LogMan::Throw::AFmt(Alloc == StackRegionIt->Ptr, "mmap returned {} instead of {}", Alloc, fmt::ptr(StackRegionIt->Ptr));
|
||||
@@ -300,7 +305,7 @@ fextl::vector<MemoryRegion> StealMemoryRegion(uintptr_t Begin, uintptr_t End) {
|
||||
|
||||
// Block remaining memory gaps
|
||||
for (auto RegionIt = Regions.begin(); RegionIt != Regions.end(); ++RegionIt) {
|
||||
auto Alloc = mmap(RegionIt->Ptr, RegionIt->Size, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
auto Alloc = ::mmap(RegionIt->Ptr, RegionIt->Size, PROT_NONE, MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0);
|
||||
|
||||
LogMan::Throw::AFmt(Alloc != MAP_FAILED, "mmap({},{:x}) failed", fmt::ptr(RegionIt->Ptr), RegionIt->Size);
|
||||
LogMan::Throw::AFmt(Alloc == RegionIt->Ptr, "mmap returned {} instead of {}", Alloc, fmt::ptr(RegionIt->Ptr));
|
||||
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
// Align UsedPages so it pads to the next page.
|
||||
// Necessary to take advantage of madvise zero page pooling.
|
||||
using FlexBitElementType = uint64_t;
|
||||
alignas(4096) FEXCore::FlexBitSet<FlexBitElementType> UsedPages;
|
||||
alignas(FEXCore::Utils::FEX_PAGE_SIZE) FEXCore::FlexBitSet<FlexBitElementType> UsedPages;
|
||||
|
||||
// This returns the size of the LiveVMARegion in addition to the flex set that tracks the used data
|
||||
// The LiveVMARegion lives at the start of the VMA region which means on initialization we need to set that
|
||||
@@ -140,7 +140,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(LiveVMARegion) == 4096, "Needs to be the size of a page");
|
||||
static_assert(sizeof(LiveVMARegion) == FEXCore::Utils::FEX_PAGE_SIZE, "Needs to be the size of a page");
|
||||
|
||||
static_assert(std::is_trivially_copyable<LiveVMARegion>::value, "Needs to be trivially copyable");
|
||||
static_assert(offsetof(LiveVMARegion, UsedPages) == sizeof(LiveVMARegion), "FlexBitSet needs to be at the end");
|
||||
@@ -168,6 +168,7 @@ private:
|
||||
LOGMAN_THROW_A_FMT(Res != -1, "Couldn't mprotect region: {} '{}' Likely occurs when running out of memory or Maximum VMAs", errno,
|
||||
strerror(errno));
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(ReservedRegion->Base), SizePlusManagedData);
|
||||
LiveVMARegion* LiveRange = new (reinterpret_cast<void*>(ReservedRegion->Base)) LiveVMARegion();
|
||||
|
||||
// Copy over the reserved data
|
||||
@@ -473,7 +474,7 @@ int OSAllocator_64Bit::Munmap(void* addr, size_t length) {
|
||||
::mmap(addr, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
}
|
||||
|
||||
(*it)->FreeSpace += FreedPages * 4096;
|
||||
(*it)->FreeSpace += FreedPages * FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
|
||||
// Set the last allocated page to the minimum of last page allocation or this slab
|
||||
// This will let us more quickly fill holes
|
||||
@@ -505,6 +506,8 @@ void OSAllocator_64Bit::AllocateMemoryRegions(fextl::vector<FEXCore::Allocator::
|
||||
// This enables the kernel to use transparent large pages in the allocator which can reduce memory pressure
|
||||
::madvise(it.Ptr, ObjectAllocSize, MADV_HUGEPAGE);
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(it.Ptr), ObjectAllocSize);
|
||||
|
||||
ObjectAlloc = new (it.Ptr) Alloc::ForwardOnlyIntrusiveArenaAllocator(it.Ptr, ObjectAllocSize);
|
||||
ReservedRegions = ObjectAlloc->new_construct(ReservedRegions, ObjectAlloc);
|
||||
LiveRegions = ObjectAlloc->new_construct(LiveRegions, ObjectAlloc);
|
||||
@@ -602,6 +605,8 @@ fextl::unique_ptr<T> make_alloc_unique(FEXCore::Allocator::MemoryRegion& Base, A
|
||||
ERROR_AND_DIE_FMT("Couldn't allocate memory region");
|
||||
}
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(ptr), MinPage);
|
||||
|
||||
// Remove the page from the base region.
|
||||
// Could be zero after this.
|
||||
Base.Size -= MinPage;
|
||||
|
||||
@@ -144,33 +144,33 @@ static __uint128_t LoadAcquire128(uint64_t Addr) {
|
||||
}
|
||||
|
||||
static uint64_t LoadAcquire64(uint64_t Addr) {
|
||||
std::atomic<uint64_t>* Atom = reinterpret_cast<std::atomic<uint64_t>*>(Addr);
|
||||
return Atom->load(std::memory_order_acquire);
|
||||
auto Atom = std::atomic_ref<uint64_t>(*reinterpret_cast<uint64_t*>(Addr));
|
||||
return Atom.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
static bool StoreCAS64(uint64_t& Expected, uint64_t Val, uint64_t Addr) {
|
||||
std::atomic<uint64_t>* Atom = reinterpret_cast<std::atomic<uint64_t>*>(Addr);
|
||||
return Atom->compare_exchange_strong(Expected, Val);
|
||||
auto Atom = std::atomic_ref<uint64_t>(*reinterpret_cast<uint64_t*>(Addr));
|
||||
return Atom.compare_exchange_strong(Expected, Val);
|
||||
}
|
||||
|
||||
static uint32_t LoadAcquire32(uint64_t Addr) {
|
||||
std::atomic<uint32_t>* Atom = reinterpret_cast<std::atomic<uint32_t>*>(Addr);
|
||||
return Atom->load(std::memory_order_acquire);
|
||||
auto Atom = std::atomic_ref<uint32_t>(*reinterpret_cast<uint32_t*>(Addr));
|
||||
return Atom.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
static bool StoreCAS32(uint32_t& Expected, uint32_t Val, uint64_t Addr) {
|
||||
std::atomic<uint32_t>* Atom = reinterpret_cast<std::atomic<uint32_t>*>(Addr);
|
||||
return Atom->compare_exchange_strong(Expected, Val);
|
||||
auto Atom = std::atomic_ref<uint32_t>(*reinterpret_cast<uint32_t*>(Addr));
|
||||
return Atom.compare_exchange_strong(Expected, Val);
|
||||
}
|
||||
|
||||
static uint8_t LoadAcquire8(uint64_t Addr) {
|
||||
std::atomic<uint8_t>* Atom = reinterpret_cast<std::atomic<uint8_t>*>(Addr);
|
||||
return Atom->load(std::memory_order_acquire);
|
||||
auto Atom = std::atomic_ref<uint8_t>(*reinterpret_cast<uint8_t*>(Addr));
|
||||
return Atom.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
static bool StoreCAS8(uint8_t& Expected, uint8_t Val, uint64_t Addr) {
|
||||
std::atomic<uint8_t>* Atom = reinterpret_cast<std::atomic<uint8_t>*>(Addr);
|
||||
return Atom->compare_exchange_strong(Expected, Val);
|
||||
auto Atom = std::atomic_ref<uint8_t>(*reinterpret_cast<uint8_t*>(Addr));
|
||||
return Atom.compare_exchange_strong(Expected, Val);
|
||||
}
|
||||
|
||||
static uint16_t DoLoad16(uint64_t Addr) {
|
||||
@@ -211,8 +211,8 @@ static uint16_t DoLoad16(uint64_t Addr) {
|
||||
uint64_t Alignment = Addr & AlignmentMask;
|
||||
Addr &= ~AlignmentMask;
|
||||
|
||||
std::atomic<uint64_t>* Atomic = reinterpret_cast<std::atomic<uint64_t>*>(Addr);
|
||||
uint64_t TmpResult = Atomic->load();
|
||||
auto Atomic = std::atomic_ref<uint64_t>(*reinterpret_cast<uint64_t*>(Addr));
|
||||
uint64_t TmpResult = Atomic.load();
|
||||
|
||||
// Zexts the result
|
||||
uint16_t Result = TmpResult >> (Alignment * 8);
|
||||
@@ -224,8 +224,8 @@ static uint16_t DoLoad16(uint64_t Addr) {
|
||||
uint64_t Alignment = Addr & AlignmentMask;
|
||||
Addr &= ~AlignmentMask;
|
||||
|
||||
std::atomic<uint32_t>* Atomic = reinterpret_cast<std::atomic<uint32_t>*>(Addr);
|
||||
uint32_t TmpResult = Atomic->load();
|
||||
auto Atomic = std::atomic_ref<uint32_t>(*reinterpret_cast<uint32_t*>(Addr));
|
||||
uint32_t TmpResult = Atomic.load();
|
||||
|
||||
// Zexts the result
|
||||
uint16_t Result = TmpResult >> (Alignment * 8);
|
||||
@@ -272,8 +272,8 @@ static uint32_t DoLoad32(uint64_t Addr) {
|
||||
uint64_t Alignment = Addr & AlignmentMask;
|
||||
Addr &= ~AlignmentMask;
|
||||
|
||||
std::atomic<uint64_t>* Atomic = reinterpret_cast<std::atomic<uint64_t>*>(Addr);
|
||||
uint64_t TmpResult = Atomic->load();
|
||||
auto Atomic = std::atomic_ref<uint64_t>(*reinterpret_cast<uint64_t*>(Addr));
|
||||
uint64_t TmpResult = Atomic.load();
|
||||
|
||||
return TmpResult >> (Alignment * 8);
|
||||
}
|
||||
@@ -465,7 +465,7 @@ static bool RunCASPAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg1, uint3
|
||||
// Fits within a 16byte region
|
||||
uint64_t Alignment = Addr & 0b1111;
|
||||
Addr &= ~0b1111ULL;
|
||||
std::atomic<__uint128_t>* Atomic128 = reinterpret_cast<std::atomic<__uint128_t>*>(Addr);
|
||||
auto Atomic128 = std::atomic_ref<__uint128_t>(*reinterpret_cast<__uint128_t*>(Addr));
|
||||
|
||||
__uint128_t Mask = ~0ULL;
|
||||
Mask <<= Alignment * 8;
|
||||
@@ -480,7 +480,7 @@ static bool RunCASPAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg1, uint3
|
||||
Expected <<= Alignment * 8;
|
||||
|
||||
while (1) {
|
||||
TmpExpected = Atomic128->load();
|
||||
TmpExpected = Atomic128.load();
|
||||
|
||||
// Set up expected
|
||||
TmpExpected &= NegMask;
|
||||
@@ -491,7 +491,7 @@ static bool RunCASPAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg1, uint3
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired;
|
||||
|
||||
bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic128.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Successful, so we are done
|
||||
return true;
|
||||
@@ -617,36 +617,6 @@ static uint64_t HandleCASPAL_ARMv8(uint32_t Instr, uintptr_t ProgramCounter, uin
|
||||
}
|
||||
}
|
||||
|
||||
static bool HandleAtomicVectorStore(uint32_t Instr, uintptr_t ProgramCounter) {
|
||||
uint32_t* PC = (uint32_t*)ProgramCounter;
|
||||
|
||||
uint32_t Size = (Instr >> 30) & 1;
|
||||
uint32_t DataReg = Instr & 0x1F;
|
||||
|
||||
if (Size == 1) {
|
||||
// 64-bit pair happens on paranoid vector stores
|
||||
// [0] ldaxp(xzr, TMP3, MemSrc); // <- Can hit SIGBUS. Overwritten with DMB
|
||||
// [1] stlxp(TMP3, TMP1, TMP2, MemSrc); // <- Can also hit SIGBUS
|
||||
// [2] cbnz(TMP3, &B); // < Overwritten with DMB
|
||||
if (DataReg == 31) {
|
||||
uint32_t NextInstr = PC[1];
|
||||
uint32_t AddrReg = (NextInstr >> 5) & 0x1F;
|
||||
DataReg = NextInstr & 0x1F;
|
||||
uint32_t DataReg2 = (NextInstr >> 10) & 0x1F;
|
||||
uint32_t STP = (0b10 << 30) | (0b101001000000000 << 15) | (DataReg2 << 10) | (AddrReg << 5) | DataReg;
|
||||
|
||||
PC[0] = DMB;
|
||||
PC[1] = STP;
|
||||
PC[2] = DMB;
|
||||
// Back up one instruction and have another go
|
||||
ClearICache(&PC[0], 12);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using CASExpectedFn = T (*)(T Src, T Expected);
|
||||
template<typename T>
|
||||
@@ -740,7 +710,7 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
// Fits within a 16byte region
|
||||
uint64_t Alignment = Addr & 0b1111;
|
||||
Addr &= ~0b1111ULL;
|
||||
std::atomic<__uint128_t>* Atomic128 = reinterpret_cast<std::atomic<__uint128_t>*>(Addr);
|
||||
auto Atomic128 = std::atomic_ref<__uint128_t>(*reinterpret_cast<__uint128_t*>(Addr));
|
||||
|
||||
__uint128_t Mask = 0xFFFF;
|
||||
Mask <<= Alignment * 8;
|
||||
@@ -749,7 +719,7 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
__uint128_t TmpDesired {};
|
||||
|
||||
while (1) {
|
||||
TmpExpected = Atomic128->load();
|
||||
TmpExpected = Atomic128.load();
|
||||
|
||||
__uint128_t Desired = DesiredFunction(TmpExpected >> (Alignment * 8), DesiredSrc);
|
||||
Desired <<= Alignment * 8;
|
||||
@@ -766,7 +736,7 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired;
|
||||
|
||||
bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic128.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Successful, so we are done
|
||||
return Expected >> (Alignment * 8);
|
||||
@@ -810,9 +780,9 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
uint64_t TmpExpected {};
|
||||
uint64_t TmpDesired {};
|
||||
|
||||
std::atomic<uint64_t>* Atomic = reinterpret_cast<std::atomic<uint64_t>*>(Addr);
|
||||
auto Atomic = std::atomic_ref<uint64_t>(*reinterpret_cast<uint64_t*>(Addr));
|
||||
while (1) {
|
||||
TmpExpected = Atomic->load();
|
||||
TmpExpected = Atomic.load();
|
||||
|
||||
uint64_t Desired = DesiredFunction(TmpExpected >> (Alignment * 8), DesiredSrc);
|
||||
Desired <<= Alignment * 8;
|
||||
@@ -829,7 +799,7 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired;
|
||||
|
||||
bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Successful, so we are done
|
||||
return Expected >> (Alignment * 8);
|
||||
@@ -873,9 +843,9 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
uint32_t TmpExpected {};
|
||||
uint32_t TmpDesired {};
|
||||
|
||||
std::atomic<uint32_t>* Atomic = reinterpret_cast<std::atomic<uint32_t>*>(Addr);
|
||||
auto Atomic = std::atomic_ref<uint32_t>(*reinterpret_cast<uint32_t*>(Addr));
|
||||
while (1) {
|
||||
TmpExpected = Atomic->load();
|
||||
TmpExpected = Atomic.load();
|
||||
|
||||
|
||||
uint32_t Desired = DesiredFunction(TmpExpected >> (Alignment * 8), DesiredSrc);
|
||||
@@ -893,7 +863,7 @@ static uint16_t DoCAS16(uint16_t DesiredSrc, uint16_t ExpectedSrc, uint64_t Addr
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired;
|
||||
|
||||
bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Successful, so we are done
|
||||
return Expected >> (Alignment * 8);
|
||||
@@ -1040,7 +1010,7 @@ static uint32_t DoCAS32(uint32_t DesiredSrc, uint32_t ExpectedSrc, uint64_t Addr
|
||||
// Fits within a 16byte region
|
||||
uint64_t Alignment = Addr & 0b1111;
|
||||
Addr &= ~0b1111ULL;
|
||||
std::atomic<__uint128_t>* Atomic128 = reinterpret_cast<std::atomic<__uint128_t>*>(Addr);
|
||||
auto Atomic128 = std::atomic_ref<__uint128_t>(*reinterpret_cast<__uint128_t*>(Addr));
|
||||
|
||||
__uint128_t Mask = ~0U;
|
||||
Mask <<= Alignment * 8;
|
||||
@@ -1049,7 +1019,7 @@ static uint32_t DoCAS32(uint32_t DesiredSrc, uint32_t ExpectedSrc, uint64_t Addr
|
||||
__uint128_t TmpDesired {};
|
||||
|
||||
while (1) {
|
||||
__uint128_t TmpActual = Atomic128->load();
|
||||
__uint128_t TmpActual = Atomic128.load();
|
||||
|
||||
__uint128_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc);
|
||||
__uint128_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc);
|
||||
@@ -1064,7 +1034,7 @@ static uint32_t DoCAS32(uint32_t DesiredSrc, uint32_t ExpectedSrc, uint64_t Addr
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired << (Alignment * 8);
|
||||
|
||||
bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic128.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Stored successfully
|
||||
return Expected;
|
||||
@@ -1108,9 +1078,9 @@ static uint32_t DoCAS32(uint32_t DesiredSrc, uint32_t ExpectedSrc, uint64_t Addr
|
||||
uint64_t TmpExpected {};
|
||||
uint64_t TmpDesired {};
|
||||
|
||||
std::atomic<uint64_t>* Atomic = reinterpret_cast<std::atomic<uint64_t>*>(Addr);
|
||||
auto Atomic = std::atomic_ref<uint64_t>(*reinterpret_cast<uint64_t*>(Addr));
|
||||
while (1) {
|
||||
uint64_t TmpActual = Atomic->load();
|
||||
uint64_t TmpActual = Atomic.load();
|
||||
|
||||
uint64_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc);
|
||||
uint64_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc);
|
||||
@@ -1125,7 +1095,7 @@ static uint32_t DoCAS32(uint32_t DesiredSrc, uint32_t ExpectedSrc, uint64_t Addr
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired << (Alignment * 8);
|
||||
|
||||
bool CASResult = Atomic->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Stored successfully
|
||||
return Expected;
|
||||
@@ -1270,7 +1240,7 @@ static uint64_t DoCAS64(uint64_t DesiredSrc, uint64_t ExpectedSrc, uint64_t Addr
|
||||
// Fits within a 16byte region
|
||||
uint64_t Alignment = Addr & AlignmentMask;
|
||||
Addr &= ~AlignmentMask;
|
||||
std::atomic<__uint128_t>* Atomic128 = reinterpret_cast<std::atomic<__uint128_t>*>(Addr);
|
||||
auto Atomic128 = std::atomic_ref<__uint128_t>(*reinterpret_cast<__uint128_t*>(Addr));
|
||||
|
||||
__uint128_t Mask = ~0ULL;
|
||||
Mask <<= Alignment * 8;
|
||||
@@ -1279,7 +1249,7 @@ static uint64_t DoCAS64(uint64_t DesiredSrc, uint64_t ExpectedSrc, uint64_t Addr
|
||||
__uint128_t TmpDesired {};
|
||||
|
||||
while (1) {
|
||||
__uint128_t TmpActual = Atomic128->load();
|
||||
__uint128_t TmpActual = Atomic128.load();
|
||||
|
||||
__uint128_t Desired = DesiredFunction(TmpActual >> (Alignment * 8), DesiredSrc);
|
||||
__uint128_t Expected = ExpectedFunction(TmpActual >> (Alignment * 8), ExpectedSrc);
|
||||
@@ -1294,7 +1264,7 @@ static uint64_t DoCAS64(uint64_t DesiredSrc, uint64_t ExpectedSrc, uint64_t Addr
|
||||
TmpDesired &= NegMask;
|
||||
TmpDesired |= Desired << (Alignment * 8);
|
||||
|
||||
bool CASResult = Atomic128->compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
bool CASResult = Atomic128.compare_exchange_strong(TmpExpected, TmpDesired);
|
||||
if (CASResult) {
|
||||
// Stored successfully
|
||||
return Expected;
|
||||
@@ -1326,9 +1296,7 @@ static uint64_t DoCAS64(uint64_t DesiredSrc, uint64_t ExpectedSrc, uint64_t Addr
|
||||
}
|
||||
}
|
||||
|
||||
static bool RunCASAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg, uint32_t ExpectedReg, uint32_t AddressReg, uint32_t* StrictSplitLockMutex) {
|
||||
uint64_t Addr = GPRs[AddressReg];
|
||||
|
||||
static std::optional<uint64_t> DoCAS(uint32_t Size, uint64_t Desired, uint64_t Expected, uint64_t Addr, uint32_t* StrictSplitLockMutex) {
|
||||
// Cross-cacheline CAS doesn't work on ARM
|
||||
// It isn't even guaranteed to work on x86
|
||||
// Intel will do a "split lock" which locks the full bus
|
||||
@@ -1341,7 +1309,7 @@ static bool RunCASAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg, uint32_
|
||||
// Only need to handle 16, 32, 64
|
||||
if (Size == 2) {
|
||||
auto Res = DoCAS16<false>(
|
||||
GPRs[DesiredReg], GPRs[ExpectedReg], Addr,
|
||||
Desired, Expected, Addr,
|
||||
[](uint16_t, uint16_t Expected) -> uint16_t {
|
||||
// Expected is just Expected
|
||||
return Expected;
|
||||
@@ -1351,16 +1319,10 @@ static bool RunCASAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg, uint32_
|
||||
return Desired;
|
||||
},
|
||||
StrictSplitLockMutex);
|
||||
|
||||
// Regardless of pass or fail
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ExpectedReg != 31) {
|
||||
GPRs[ExpectedReg] = Res;
|
||||
}
|
||||
return true;
|
||||
return Res;
|
||||
} else if (Size == 4) {
|
||||
auto Res = DoCAS32<false>(
|
||||
GPRs[DesiredReg], GPRs[ExpectedReg], Addr,
|
||||
Desired, Expected, Addr,
|
||||
[](uint32_t, uint32_t Expected) -> uint32_t {
|
||||
// Expected is just Expected
|
||||
return Expected;
|
||||
@@ -1370,16 +1332,10 @@ static bool RunCASAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg, uint32_
|
||||
return Desired;
|
||||
},
|
||||
StrictSplitLockMutex);
|
||||
|
||||
// Regardless of pass or fail
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ExpectedReg != 31) {
|
||||
GPRs[ExpectedReg] = Res;
|
||||
}
|
||||
return true;
|
||||
return Res;
|
||||
} else if (Size == 8) {
|
||||
auto Res = DoCAS64<false>(
|
||||
GPRs[DesiredReg], GPRs[ExpectedReg], Addr,
|
||||
Desired, Expected, Addr,
|
||||
[](uint64_t, uint64_t Expected) -> uint64_t {
|
||||
// Expected is just Expected
|
||||
return Expected;
|
||||
@@ -1389,16 +1345,24 @@ static bool RunCASAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg, uint32_
|
||||
return Desired;
|
||||
},
|
||||
StrictSplitLockMutex);
|
||||
|
||||
// Regardless of pass or fail
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ExpectedReg != 31) {
|
||||
GPRs[ExpectedReg] = Res;
|
||||
}
|
||||
return true;
|
||||
return Res;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static bool RunCASAL(uint64_t* GPRs, uint32_t Size, uint32_t DesiredReg, uint32_t ExpectedReg, uint32_t AddressReg, uint32_t* StrictSplitLockMutex) {
|
||||
std::optional<uint64_t> Res = DoCAS(Size, GPRs[DesiredReg], GPRs[ExpectedReg], GPRs[AddressReg], StrictSplitLockMutex);
|
||||
if (!Res.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Regardless of pass or fail
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ExpectedReg != 31) {
|
||||
GPRs[ExpectedReg] = *Res;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleCASAL(uint64_t* GPRs, uint32_t Instr, uint32_t* StrictSplitLockMutex) {
|
||||
@@ -1560,38 +1524,43 @@ static bool HandleAtomicMemOp(uint32_t Instr, uint64_t* GPRs, uint32_t* StrictSp
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool HandleAtomicLoad(uint32_t Instr, uint64_t* GPRs, int64_t Offset) {
|
||||
static bool HandleAtomicLoad(uint32_t Instr, uint64_t* GPRs, int64_t Offset, Core::UnalignedExclusiveStore* Store = nullptr) {
|
||||
uint32_t Size = 1 << (Instr >> 30);
|
||||
|
||||
uint32_t ResultReg = Instr & 0b11111;
|
||||
uint32_t AddressReg = (Instr >> 5) & 0b11111;
|
||||
|
||||
uint64_t Addr = GPRs[AddressReg] + Offset;
|
||||
uint64_t Res;
|
||||
|
||||
if (Size == 2) {
|
||||
auto Res = DoLoad16(Addr);
|
||||
Res = DoLoad16(Addr);
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ResultReg != 31) {
|
||||
GPRs[ResultReg] = Res;
|
||||
}
|
||||
return true;
|
||||
} else if (Size == 4) {
|
||||
auto Res = DoLoad32(Addr);
|
||||
Res = DoLoad32(Addr);
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ResultReg != 31) {
|
||||
GPRs[ResultReg] = Res;
|
||||
}
|
||||
return true;
|
||||
} else if (Size == 8) {
|
||||
auto Res = DoLoad64(Addr);
|
||||
Res = DoLoad64(Addr);
|
||||
// We set the result register if it isn't a zero register
|
||||
if (ResultReg != 31) {
|
||||
GPRs[ResultReg] = Res;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (Store) {
|
||||
Store->Addr = Addr;
|
||||
Store->Store = Res;
|
||||
Store->Size = Size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleAtomicStore(uint32_t Instr, uint64_t* GPRs, int64_t Offset, uint32_t* StrictSplitLockMutex) {
|
||||
@@ -1952,8 +1921,8 @@ static uint64_t HandleAtomicLoadstoreExclusive(uintptr_t ProgramCounter, uint64_
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::optional<int32_t>
|
||||
HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandlerType HandleType, uintptr_t ProgramCounter, uint64_t* GPRs) {
|
||||
std::optional<int32_t> HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandlerType HandleType,
|
||||
uintptr_t ProgramCounter, uint64_t* GPRs, bool IsJIT) {
|
||||
#ifdef _M_ARM_64
|
||||
constexpr bool is_arm64 = true;
|
||||
#else
|
||||
@@ -1977,8 +1946,7 @@ HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandl
|
||||
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
|
||||
uint32_t* StrictSplitLockMutex {CTX->Config.StrictInProcessSplitLocks ? &CTX->StrictSplitLockMutex : nullptr};
|
||||
|
||||
// ParanoidTSO path doesn't modify any code.
|
||||
if (HandleType == UnalignedHandlerType::Paranoid) [[unlikely]] {
|
||||
if (!IsJIT) [[unlikely]] {
|
||||
if ((Instr & LDAXR_MASK) == LDAR_INST || // LDAR*
|
||||
(Instr & LDAXR_MASK) == LDAPR_INST) { // LDAPR*
|
||||
if (ArchHelpers::Arm64::HandleAtomicLoad(Instr, GPRs, 0)) {
|
||||
@@ -2016,7 +1984,29 @@ HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandl
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDLUR*: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]);
|
||||
return std::nullopt;
|
||||
}
|
||||
} else if ((Instr & ArchHelpers::Arm64::LDAXR_MASK) == ArchHelpers::Arm64::LDAXR_INST) { // LDAXR*
|
||||
if (ArchHelpers::Arm64::HandleAtomicLoad(Instr, GPRs, 0, &Thread->ExclusiveStore)) {
|
||||
return 4;
|
||||
}
|
||||
} else if ((Instr & ArchHelpers::Arm64::STLXR_MASK) == ArchHelpers::Arm64::STLXR_INST) { // STLXR*
|
||||
uint32_t StatusReg = Instr << 11 >> 27;
|
||||
// // Emulate exclusive store by validating the address and value against the last unaligned LDAXR*.
|
||||
if (GPRs[AddrReg] != Thread->ExclusiveStore.Addr || Size > Thread->ExclusiveStore.Size) {
|
||||
if (StatusReg != 31) {
|
||||
GPRs[StatusReg] = 1;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
if (std::optional<uint64_t> Prev =
|
||||
DoCAS(Size, DataReg == 31 ? 0 : GPRs[DataReg], Thread->ExclusiveStore.Store, GPRs[AddrReg], StrictSplitLockMutex)) {
|
||||
if (StatusReg != 31) {
|
||||
GPRs[StatusReg] = !!memcmp(&Thread->ExclusiveStore.Store, &*Prev, Size);
|
||||
}
|
||||
Thread->ExclusiveStore.Size = 0;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto Frame = Thread->CurrentFrame;
|
||||
@@ -2068,6 +2058,9 @@ HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandl
|
||||
if (BytesToSkip) {
|
||||
// Skip this instruction now
|
||||
return BytesToSkip;
|
||||
} else {
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS CASPAL: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2131,19 +2124,6 @@ HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandl
|
||||
ClearICache(&PC[-1], 8);
|
||||
// Back up one instruction and have another go
|
||||
return -4;
|
||||
} else if ((Instr & ArchHelpers::Arm64::LDAXP_MASK) == ArchHelpers::Arm64::LDAXP_INST) { // LDAXP
|
||||
/// This is handling the case of paranoid ARMv8.0-a atomic stores.
|
||||
/// This backpatches the ldaxp+stlxp+cbnz if the previous `HandleCASPAL_ARMv8` didn't handle the case.
|
||||
if (ArchHelpers::Arm64::HandleAtomicVectorStore(Instr, ProgramCounter)) {
|
||||
return 0;
|
||||
} else {
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS LDAXP: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]);
|
||||
return std::nullopt;
|
||||
}
|
||||
} else if ((Instr & ArchHelpers::Arm64::STLXP_MASK) == ArchHelpers::Arm64::STLXP_INST) { // STLXP
|
||||
// Should not trigger - middle of an LDAXP/STAXP pair.
|
||||
LogMan::Msg::EFmt("Unhandled JIT SIGBUS STLXP: PC: 0x{:x} Instruction: 0x{:08x}\n", ProgramCounter, PC[0]);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check if another thread backpatched this instruction before this thread got here
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include <FEXCore/Utils/LongJump.h>
|
||||
|
||||
namespace FEXCore::LongJump {
|
||||
#if defined(_M_ARM_64)
|
||||
[[nodiscard]]
|
||||
FEX_DEFAULT_VISIBILITY FEX_NAKED uint64_t SetJump(JumpBuf& Buffer) {
|
||||
__asm volatile(R"(
|
||||
// x0 contains the jumpbuffer
|
||||
stp x19, x20, [x0, #( 0 * 8)];
|
||||
stp x21, x22, [x0, #( 2 * 8)];
|
||||
stp x23, x24, [x0, #( 4 * 8)];
|
||||
stp x25, x26, [x0, #( 6 * 8)];
|
||||
stp x27, x28, [x0, #( 8 * 8)];
|
||||
stp x29, x30, [x0, #(10 * 8)];
|
||||
|
||||
// FPRs
|
||||
stp d8, d9, [x0, #(12 * 8)];
|
||||
stp d10, d11, [x0, #(14 * 8)];
|
||||
stp d12, d13, [x0, #(16 * 8)];
|
||||
stp d14, d15, [x0, #(18 * 8)];
|
||||
|
||||
// Move SP in to a temporary to store.
|
||||
mov x1, sp;
|
||||
str x1, [x0, #(20 * 8)];
|
||||
|
||||
// Return zero to signify this is the SetJump.
|
||||
mov x0, #0;
|
||||
ret;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
FEX_DEFAULT_VISIBILITY FEX_NAKED void LongJump(JumpBuf& Buffer, uint64_t Value) {
|
||||
__asm volatile(R"(
|
||||
// x0 contains the jumpbuffer
|
||||
ldp x19, x20, [x0, #( 0 * 8)];
|
||||
ldp x21, x22, [x0, #( 2 * 8)];
|
||||
ldp x23, x24, [x0, #( 4 * 8)];
|
||||
ldp x25, x26, [x0, #( 6 * 8)];
|
||||
ldp x27, x28, [x0, #( 8 * 8)];
|
||||
ldp x29, x30, [x0, #(10 * 8)];
|
||||
|
||||
// FPRs
|
||||
ldp d8, d9, [x0, #(12 * 8)];
|
||||
ldp d10, d11, [x0, #(14 * 8)];
|
||||
ldp d12, d13, [x0, #(16 * 8)];
|
||||
ldp d14, d15, [x0, #(18 * 8)];
|
||||
|
||||
// Load SP in to temporary then move
|
||||
ldr x0, [x0, #(20 * 8)];
|
||||
mov sp, x0;
|
||||
|
||||
// Move value in to result register
|
||||
mov x0, x1;
|
||||
ret;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
#else
|
||||
[[nodiscard]]
|
||||
FEX_DEFAULT_VISIBILITY FEX_NAKED uint64_t SetJump(JumpBuf& Buffer) {
|
||||
__asm volatile(R"(
|
||||
.intel_syntax noprefix;
|
||||
// rdi contains the jumpbuffer
|
||||
mov [rdi + (0 * 8)], rbx;
|
||||
mov [rdi + (1 * 8)], rsp;
|
||||
mov [rdi + (2 * 8)], rbp;
|
||||
mov [rdi + (3 * 8)], r12;
|
||||
mov [rdi + (4 * 8)], r13;
|
||||
mov [rdi + (5 * 8)], r14;
|
||||
mov [rdi + (6 * 8)], r15;
|
||||
|
||||
// Return address is on the stack, load it and store
|
||||
mov rsi, [rsp];
|
||||
mov [rdi + (7 * 8)], rsi;
|
||||
|
||||
// Return zero to signify this is the SetJump.
|
||||
mov rax, 0;
|
||||
ret;
|
||||
|
||||
.att_syntax prefix;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
FEX_DEFAULT_VISIBILITY FEX_NAKED void LongJump(JumpBuf& Buffer, uint64_t Value) {
|
||||
__asm volatile(R"(
|
||||
.intel_syntax noprefix;
|
||||
// rdi contains the jumpbuffer
|
||||
mov rbx, [rdi + (0 * 8)];
|
||||
mov rsp, [rdi + (1 * 8)];
|
||||
mov rbp, [rdi + (2 * 8)];
|
||||
mov r12, [rdi + (3 * 8)];
|
||||
mov r13, [rdi + (4 * 8)];
|
||||
mov r14, [rdi + (5 * 8)];
|
||||
mov r15, [rdi + (6 * 8)];
|
||||
|
||||
// Move value in to result register
|
||||
mov rax, rsi;
|
||||
|
||||
// Pop the dead return address off the stack
|
||||
pop rsi;
|
||||
|
||||
// Load the original return address from the jumpbuffer
|
||||
mov rsi, [rdi + (7 * 8)];
|
||||
|
||||
// Return using a jump
|
||||
jmp rsi;
|
||||
|
||||
.att_syntax prefix;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace FEXCore::LongJump
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/Profiler.h>
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/fextl/fmt.h>
|
||||
#include <FEXCore/fextl/string.h>
|
||||
|
||||
@@ -70,6 +71,10 @@ static std::array<const char*, 2> TraceFSDirectories {
|
||||
};
|
||||
|
||||
void Init() {
|
||||
FEX_CONFIG_OPT(EnableGpuvisProfiling, ENABLEGPUVISPROFILING);
|
||||
if (!EnableGpuvisProfiling()) {
|
||||
return;
|
||||
}
|
||||
for (auto Path : TraceFSDirectories) {
|
||||
#ifdef _WIN32
|
||||
constexpr auto flags = O_WRONLY;
|
||||
|
||||
@@ -125,8 +125,8 @@ static inline uint64_t WFELoadAtomic(uint64_t* Futex) {
|
||||
|
||||
template<typename T, typename TT = T>
|
||||
static inline void Wait(T* Futex, TT ExpectedValue) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
T Result = AtomicFutex->load();
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
T Result = AtomicFutex.load();
|
||||
|
||||
// Early exit if possible.
|
||||
if (Result == ExpectedValue) {
|
||||
@@ -149,9 +149,9 @@ template void Wait<uint64_t>(uint64_t*, uint64_t);
|
||||
|
||||
template<typename T, typename TT>
|
||||
static inline bool Wait(T* Futex, TT ExpectedValue, const std::chrono::nanoseconds& Timeout) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
|
||||
T Result = AtomicFutex->load();
|
||||
T Result = AtomicFutex.load();
|
||||
|
||||
// Early exit if possible.
|
||||
if (Result == ExpectedValue) {
|
||||
@@ -187,8 +187,8 @@ template bool Wait<uint64_t>(uint64_t*, uint64_t, const std::chrono::nanoseconds
|
||||
#else
|
||||
template<typename T, typename TT>
|
||||
static inline void Wait(T* Futex, TT ExpectedValue) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
T Result = AtomicFutex->load();
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
T Result = AtomicFutex.load();
|
||||
|
||||
// Early exit if possible.
|
||||
if (Result == ExpectedValue) {
|
||||
@@ -196,15 +196,15 @@ static inline void Wait(T* Futex, TT ExpectedValue) {
|
||||
}
|
||||
|
||||
do {
|
||||
Result = AtomicFutex->load();
|
||||
Result = AtomicFutex.load();
|
||||
} while (Result != ExpectedValue);
|
||||
}
|
||||
|
||||
template<typename T, typename TT>
|
||||
static inline bool Wait(T* Futex, TT ExpectedValue, const std::chrono::nanoseconds& Timeout) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
|
||||
T Result = AtomicFutex->load();
|
||||
T Result = AtomicFutex.load();
|
||||
|
||||
// Early exit if possible.
|
||||
if (Result == ExpectedValue) {
|
||||
@@ -214,7 +214,7 @@ static inline bool Wait(T* Futex, TT ExpectedValue, const std::chrono::nanosecon
|
||||
const auto Begin = std::chrono::high_resolution_clock::now();
|
||||
|
||||
do {
|
||||
Result = AtomicFutex->load();
|
||||
Result = AtomicFutex.load();
|
||||
|
||||
const auto CurrentCycleCounter = std::chrono::high_resolution_clock::now();
|
||||
if ((CurrentCycleCounter - Begin) >= Timeout) {
|
||||
@@ -230,12 +230,12 @@ static inline bool Wait(T* Futex, TT ExpectedValue, const std::chrono::nanosecon
|
||||
|
||||
template<typename T>
|
||||
static inline void lock(T* Futex) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
T Expected {};
|
||||
T Desired {1};
|
||||
|
||||
// Try to CAS immediately.
|
||||
if (AtomicFutex->compare_exchange_strong(Expected, Desired)) {
|
||||
if (AtomicFutex.compare_exchange_strong(Expected, Desired)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,17 +243,17 @@ static inline void lock(T* Futex) {
|
||||
// Wait until the futex is unlocked.
|
||||
Wait(Futex, 0);
|
||||
Expected = 0;
|
||||
} while (!AtomicFutex->compare_exchange_strong(Expected, Desired));
|
||||
} while (!AtomicFutex.compare_exchange_strong(Expected, Desired));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static inline bool try_lock(T* Futex) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
T Expected {};
|
||||
T Desired {1};
|
||||
|
||||
// Try to CAS immediately.
|
||||
if (AtomicFutex->compare_exchange_strong(Expected, Desired)) {
|
||||
if (AtomicFutex.compare_exchange_strong(Expected, Desired)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -262,8 +262,8 @@ static inline bool try_lock(T* Futex) {
|
||||
|
||||
template<typename T>
|
||||
static inline void unlock(T* Futex) {
|
||||
std::atomic<T>* AtomicFutex = reinterpret_cast<std::atomic<T>*>(Futex);
|
||||
AtomicFutex->store(0);
|
||||
auto AtomicFutex = std::atomic_ref<T>(*Futex);
|
||||
AtomicFutex.store(0);
|
||||
}
|
||||
|
||||
#undef SPINLOOP_8BIT
|
||||
|
||||
@@ -42,9 +42,6 @@ enum OperatingMode {
|
||||
|
||||
using CodeRangeInvalidationFn = std::function<void(uint64_t start, uint64_t Length)>;
|
||||
|
||||
// Nested vector of guest block entrypoints
|
||||
using InvalidatedEntryAccumulator = fextl::vector<fextl::vector<uint64_t>>;
|
||||
|
||||
using CustomIREntrypointHandler = std::function<void(uintptr_t Entrypoint, IR::IREmitter*)>;
|
||||
|
||||
using ExitHandler = std::function<void(Core::InternalThreadState* Thread)>;
|
||||
@@ -141,8 +138,9 @@ public:
|
||||
virtual AbstractCodeCache& GetCodeCache() = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void ClearCodeCache(FEXCore::Core::InternalThreadState* Thread, bool NewCodeBuffer = true) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void InvalidateGuestCodeRange(
|
||||
FEXCore::Core::InternalThreadState* Thread, InvalidatedEntryAccumulator& Accumulator, uint64_t Start, uint64_t Length) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void InvalidateCodeBuffersCodeRange(uint64_t Start, uint64_t Length) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void
|
||||
InvalidateThreadCachedCodeRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::ForkableSharedMutex& GetCodeInvalidationMutex() = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void
|
||||
|
||||
@@ -104,6 +104,10 @@ struct CPUState {
|
||||
uint64_t avx_high[16][2];
|
||||
|
||||
uint64_t gregs[16] {};
|
||||
uint64_t L1Pointer {};
|
||||
uint64_t L1Mask {};
|
||||
uint64_t callret_sp {};
|
||||
uint64_t _pad1 {};
|
||||
XMMRegs xmm {};
|
||||
|
||||
// Raw segment register indexes
|
||||
@@ -116,8 +120,6 @@ struct CPUState {
|
||||
uint64_t gs_cached {};
|
||||
uint64_t fs_cached {};
|
||||
uint8_t flags[48] {};
|
||||
uint64_t callret_sp {};
|
||||
uint64_t _pad1 {};
|
||||
uint64_t mm[8][2] {};
|
||||
|
||||
// 32bit x86 state
|
||||
@@ -247,6 +249,8 @@ static_assert(offsetof(CPUState, xmm) % 32 == 0, "xmm needs to be 256-bit aligne
|
||||
static_assert(offsetof(CPUState, mm) % 16 == 0, "mm needs to be 128-bit aligned!");
|
||||
static_assert(offsetof(CPUState, gregs[15]) <= 504, "gregs maximum offset must be <= 504 for ldp/stp to work");
|
||||
static_assert(offsetof(CPUState, DeferredSignalRefCount) % 8 == 0, "Needs to be 8-byte aligned");
|
||||
static_assert(offsetof(CPUState, L1Pointer) <= 504, "This needs to be <= 504 for ldp");
|
||||
static_assert(offsetof(CPUState, L1Mask) == (offsetof(CPUState, L1Pointer) + 8), "These two variables are paired");
|
||||
|
||||
struct InternalThreadState;
|
||||
|
||||
@@ -355,7 +359,6 @@ struct JITPointers {
|
||||
uint64_t GuestSignal_SIGSEGV {};
|
||||
uint64_t SignalReturnHandler {};
|
||||
uint64_t SignalReturnHandlerRT {};
|
||||
uint64_t L1Pointer {};
|
||||
uint64_t L2Pointer {};
|
||||
/** @} */
|
||||
|
||||
|
||||
@@ -80,7 +80,14 @@ private:
|
||||
static_assert(!std::is_move_constructible_v<NonMovableUniquePtr<int>>);
|
||||
static_assert(!std::is_move_assignable_v<NonMovableUniquePtr<int>>);
|
||||
|
||||
struct InternalThreadState : public FEXCore::Allocator::FEXAllocOperators {
|
||||
// Store used for unaligned LDAXR*/STLXR* emulation.
|
||||
struct UnalignedExclusiveStore {
|
||||
uint64_t Addr;
|
||||
uint64_t Store;
|
||||
uint8_t Size;
|
||||
};
|
||||
|
||||
struct alignas(FEXCore::Utils::FEX_PAGE_SIZE) InternalThreadState : public FEXCore::Allocator::FEXAllocOperators {
|
||||
FEXCore::Core::CpuStateFrame* const CurrentFrame = &BaseFrameState;
|
||||
|
||||
FEXCore::Context::Context* const CTX;
|
||||
@@ -101,6 +108,8 @@ struct InternalThreadState : public FEXCore::Allocator::FEXAllocOperators {
|
||||
// This pointer is owned by the frontend.
|
||||
FEXCore::SHMStats::ThreadStats* ThreadStats {};
|
||||
|
||||
UnalignedExclusiveStore ExclusiveStore;
|
||||
|
||||
///< Data pointer for exclusive use by the frontend
|
||||
void* FrontendPtr;
|
||||
|
||||
@@ -116,8 +125,9 @@ struct InternalThreadState : public FEXCore::Allocator::FEXAllocOperators {
|
||||
alignas(FEXCore::Utils::FEX_PAGE_SIZE) uint8_t InterruptFaultPage[FEXCore::Utils::FEX_PAGE_SIZE];
|
||||
};
|
||||
static_assert(std::is_standard_layout_v<FEXCore::Core::InternalThreadState>);
|
||||
static_assert(
|
||||
(offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState)) < 4096,
|
||||
"Fault page is outside of immediate range from CPU state");
|
||||
static_assert((offsetof(FEXCore::Core::InternalThreadState, InterruptFaultPage) - offsetof(FEXCore::Core::InternalThreadState, BaseFrameState)) <
|
||||
FEXCore::Utils::FEX_PAGE_SIZE,
|
||||
"Fault page is outside of immediate range from CPU state");
|
||||
static_assert(sizeof(FEXCore::Core::InternalThreadState) == (FEXCore::Utils::FEX_PAGE_SIZE * 2));
|
||||
|
||||
} // namespace FEXCore::Core
|
||||
@@ -54,10 +54,6 @@ public:
|
||||
virtual ~SyscallHandler() = default;
|
||||
|
||||
virtual uint64_t HandleSyscall(FEXCore::Core::CpuStateFrame* Frame, FEXCore::HLE::SyscallArguments* Args) = 0;
|
||||
virtual SyscallABI GetSyscallABI(uint64_t Syscall) = 0;
|
||||
virtual FEXCore::IR::SyscallFlags GetSyscallFlags(uint64_t Syscall) const {
|
||||
return FEXCore::IR::SyscallFlags::DEFAULT;
|
||||
}
|
||||
|
||||
SyscallOSABI GetOSABI() const {
|
||||
return OSABI;
|
||||
|
||||
@@ -8,26 +8,6 @@
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
enum class SyscallFlags : uint8_t {
|
||||
DEFAULT = 0,
|
||||
// Syscalldoesn't care about CPUState being serialized up to the syscall instruction.
|
||||
// Means dead code elimination can optimize through a syscall operation.
|
||||
OPTIMIZETHROUGH = 1 << 0,
|
||||
// Syscall only reads the passed in arguments. Doesn't read CPUState.
|
||||
NOSYNCSTATEONENTRY = 1 << 1,
|
||||
// Syscall doesn't return. Code generation after syscall return can be removed.
|
||||
NORETURN = 1 << 2,
|
||||
// Syscall doesn't have any side-effects, so if the result isn't used then it can be removed.
|
||||
NOSIDEEFFECTS = 1 << 3,
|
||||
// Syscall doesn't return a result.
|
||||
// Means the resulting register shouldn't be written (Usually RAX).
|
||||
// Usually used with !NOSYNCSTATEONENTRY, so the syscall can modify CPU state entirely.
|
||||
// Then on return FEXCore picks up the new state.
|
||||
NORETURNEDRESULT = 1 << 4,
|
||||
};
|
||||
|
||||
FEX_DEF_NUM_OPS(SyscallFlags)
|
||||
|
||||
// This enum of named vector constants are linked to an array in CPUBackend.cpp.
|
||||
// This is used with the IROp `LoadNamedVectorConstant` to load a vector constant
|
||||
// that would otherwise be costly to materialize.
|
||||
|
||||
@@ -82,12 +82,15 @@ inline bool VirtualProtect(void* Ptr, size_t Size, ProtectOptions options) {
|
||||
return ::VirtualProtect(Ptr, Size, prot, nullptr) == 0;
|
||||
}
|
||||
|
||||
inline void VirtualName(const char*, void*, size_t) {}
|
||||
|
||||
#else
|
||||
using MMAP_Hook = void* (*)(void*, size_t, int, int, int, off_t);
|
||||
using MUNMAP_Hook = int (*)(void*, size_t);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY extern MMAP_Hook mmap;
|
||||
FEX_DEFAULT_VISIBILITY extern MUNMAP_Hook munmap;
|
||||
FEX_DEFAULT_VISIBILITY extern void VirtualName(const char* Name, void* Ptr, size_t Size);
|
||||
|
||||
// All commit parameters are ignored here, they are unnecessary as Linux supports overcommit
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ struct InternalThreadState;
|
||||
|
||||
namespace FEXCore::ArchHelpers::Arm64 {
|
||||
enum class UnalignedHandlerType {
|
||||
///< Don't backpatch code, instead handle inside SIGBUS handler.
|
||||
Paranoid,
|
||||
///< Backpatch unaligned access to half-barrier based atomic.
|
||||
HalfBarrier,
|
||||
///< Backpatch unaligned access to non-atomic.
|
||||
@@ -26,7 +24,7 @@ enum class UnalignedHandlerType {
|
||||
* This is an OS agnostic handler where the frontend must provide FEXCore with the information necessary to know if this is safe.
|
||||
* This does not check if the PC is within a JIT code buffer, the frontend must provide that safety with `CPUBackend::IsAddressInCodeBuffer`.
|
||||
*
|
||||
* @param ParanoidTSO If the unaligned fault needs to handled directly or can be backpatched.
|
||||
* @param HandleType Type of TSO handling to use.
|
||||
* @param ProgramCounter The location in memory for the instruction that did the access
|
||||
* @param GPRs The array of GPRs from the signal context. This will be modified and the host context needs to be updated on signal return.
|
||||
*
|
||||
@@ -34,6 +32,6 @@ enum class UnalignedHandlerType {
|
||||
* by. FEXCore will return a positive or negative offset depending on internal handling.
|
||||
*/
|
||||
[[nodiscard]]
|
||||
FEX_DEFAULT_VISIBILITY std::optional<int32_t>
|
||||
HandleUnalignedAccess(FEXCore::Core::InternalThreadState* Thread, UnalignedHandlerType HandleType, uintptr_t ProgramCounter, uint64_t* GPRs);
|
||||
FEX_DEFAULT_VISIBILITY std::optional<int32_t> HandleUnalignedAccess(
|
||||
FEXCore::Core::InternalThreadState* Thread, UnalignedHandlerType HandleType, uintptr_t ProgramCounter, uint64_t* GPRs, bool IsJIT = true);
|
||||
} // namespace FEXCore::ArchHelpers::Arm64
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Reimplementation of longjmp without glibc fortification checks.
|
||||
// This is useful to avoid false positives reported by glibc.
|
||||
namespace FEXCore::LongJump {
|
||||
// JumpBuf definition needs to be public because the frontend needs to understand it.
|
||||
#if defined(_M_ARM_64)
|
||||
struct JumpBuf {
|
||||
// All the registers that are required by AAPCS64 to save.
|
||||
// GPRs
|
||||
// X19, X20, X21, X22,
|
||||
// X23, X24, X25, X26,
|
||||
// X27, X28, X29, X30,
|
||||
//
|
||||
// Lower 64-bits:
|
||||
// V8, V9, V10, V11,
|
||||
// V12, V13, V14, V15,
|
||||
//
|
||||
// SP,
|
||||
uint64_t Registers[21];
|
||||
};
|
||||
#else
|
||||
struct JumpBuf {
|
||||
// Registers to preserve
|
||||
// RBX, RSP, RBP, R12, R13, R14, R15,
|
||||
// <return address>
|
||||
uint64_t Registers[8];
|
||||
};
|
||||
#endif
|
||||
|
||||
[[nodiscard]] FEX_DEFAULT_VISIBILITY uint64_t SetJump(JumpBuf& Buffer);
|
||||
[[noreturn]] FEX_DEFAULT_VISIBILITY void LongJump(JumpBuf& Buffer, uint64_t Value);
|
||||
} // namespace FEXCore::LongJump
|
||||
@@ -39,10 +39,12 @@ enum class AppType : uint8_t {
|
||||
WIN_WOW64,
|
||||
};
|
||||
|
||||
// Only append new members to the end of {ThreadStatsHeader, ThreadStats} to allow old tools time to support new information.
|
||||
// FEX isn't guaranteeing /not/ breaking compatibility with versions, but trying to not cause too much churn.
|
||||
struct ThreadStatsHeader {
|
||||
uint8_t Version;
|
||||
AppType app_type;
|
||||
uint8_t _pad[2];
|
||||
uint16_t ThreadStatsSize;
|
||||
char fex_version[48];
|
||||
std::atomic<uint32_t> Head;
|
||||
std::atomic<uint32_t> Size;
|
||||
@@ -61,8 +63,17 @@ struct ThreadStats {
|
||||
uint64_t AccumulatedSIGBUSCount;
|
||||
uint64_t AccumulatedSMCCount;
|
||||
uint64_t AccumulatedFloatFallbackCount;
|
||||
|
||||
uint64_t AccumulatedCacheMissCount;
|
||||
uint64_t AccumulatedCacheReadLockTime;
|
||||
uint64_t AccumulatedCacheWriteLockTime;
|
||||
|
||||
uint64_t AccumulatedJITCount;
|
||||
};
|
||||
|
||||
// Ensure 16-byte alignment to take advantage of ARM single-copy atomicity.
|
||||
static_assert(sizeof(ThreadStats) % 16 == 0, "Needs to be 16-byte aligned!");
|
||||
|
||||
template<typename T, size_t FlatOffset = 0>
|
||||
class AccumulationBlock final {
|
||||
public:
|
||||
|
||||
@@ -380,6 +380,8 @@ private:
|
||||
class PooledAllocatorVirtual final : public IntrusivePooledAllocator {
|
||||
public:
|
||||
PooledAllocatorVirtual() = default;
|
||||
PooledAllocatorVirtual(const char* Name)
|
||||
: Name {Name} {}
|
||||
|
||||
virtual ~PooledAllocatorVirtual() {
|
||||
FreeAllBuffers();
|
||||
@@ -387,12 +389,18 @@ public:
|
||||
|
||||
private:
|
||||
void* Alloc(size_t Size) override {
|
||||
return FEXCore::Allocator::VirtualAlloc(Size);
|
||||
auto Result = FEXCore::Allocator::VirtualAlloc(Size);
|
||||
if (Name) {
|
||||
FEXCore::Allocator::VirtualName(Name, Result, Size);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Free(void* Ptr, size_t Size) override {
|
||||
FEXCore::Allocator::VirtualFree(Ptr, Size);
|
||||
}
|
||||
|
||||
const char* Name {};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#pragma once
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/TypeDefines.h>
|
||||
#include <FEXCore/fextl/allocator.h>
|
||||
#include <FEXCore/fextl/list.h>
|
||||
|
||||
#include <memory_resource>
|
||||
#include <fmt/format.h>
|
||||
@@ -26,6 +28,90 @@ namespace pmr {
|
||||
|
||||
FEX_DEFAULT_VISIBILITY std::pmr::memory_resource* get_default_resource();
|
||||
|
||||
/**
|
||||
* @brief A `std::pmr::monotonic_buffer_resource` compatible class.
|
||||
*
|
||||
* Allocates internal buffers on page boundaries and names them for buffer tracking.
|
||||
*/
|
||||
class named_monotonic_page_buffer_resource final : public std::pmr::memory_resource {
|
||||
public:
|
||||
explicit named_monotonic_page_buffer_resource(const char* Name)
|
||||
: Name {Name} {}
|
||||
|
||||
void release() noexcept {
|
||||
for (auto& Iter : Buffers) {
|
||||
FEXCore::Allocator::VirtualFree(Iter.Buffer, Iter.BufferSize);
|
||||
}
|
||||
Buffers.clear();
|
||||
|
||||
CurrentBufferRemaining = 0;
|
||||
CurrentAllocationSize = FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
}
|
||||
|
||||
protected:
|
||||
void* do_allocate(std::size_t bytes, std::size_t alignment) override {
|
||||
LOGMAN_THROW_A_FMT(bytes != 0, "Nope");
|
||||
LOGMAN_THROW_A_FMT(alignment <= FEXCore::Utils::FEX_PAGE_SIZE, "Nope");
|
||||
|
||||
// Wow, an actual use case of std::align in the wild.
|
||||
void* NewPointer = std::align(alignment, bytes, CurrentBuffer, CurrentBufferRemaining);
|
||||
if (!NewPointer) [[unlikely]] {
|
||||
AllocateNewBuffer(bytes, alignment);
|
||||
NewPointer = CurrentBuffer;
|
||||
}
|
||||
|
||||
CurrentBuffer = static_cast<char*>(CurrentBuffer) + bytes;
|
||||
CurrentBufferRemaining -= bytes;
|
||||
|
||||
return NewPointer;
|
||||
}
|
||||
|
||||
void do_deallocate(void*, std::size_t, std::size_t) override {
|
||||
// Explicit no-op.
|
||||
}
|
||||
|
||||
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
|
||||
return this == &other;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* Name;
|
||||
|
||||
// Allocate a new buffer that can at least fit the passed in bytes with alignment.
|
||||
void AllocateNewBuffer(std::size_t bytes, std::size_t) {
|
||||
bytes = FEXCore::AlignUp(bytes, CurrentAllocationSize);
|
||||
void* Ptr = FEXCore::Allocator::VirtualAlloc(bytes);
|
||||
if (Name) {
|
||||
FEXCore::Allocator::VirtualName(Name, Ptr, bytes);
|
||||
}
|
||||
|
||||
Buffers.emplace_back(BufferData {
|
||||
.Buffer = Ptr,
|
||||
.BufferSize = bytes,
|
||||
});
|
||||
|
||||
CurrentBuffer = Ptr;
|
||||
CurrentBufferRemaining = bytes;
|
||||
|
||||
// Multiply the allocation size by 1.5 for the next allocation
|
||||
// Avoid double math because of ugly conversions.
|
||||
CurrentAllocationSize = FEXCore::AlignUp(CurrentAllocationSize + (CurrentAllocationSize >> 1), FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
}
|
||||
|
||||
// Current buffer management.
|
||||
void* CurrentBuffer {};
|
||||
size_t CurrentBufferRemaining {};
|
||||
|
||||
struct BufferData final {
|
||||
void* Buffer;
|
||||
size_t BufferSize;
|
||||
};
|
||||
|
||||
fextl::list<BufferData> Buffers {};
|
||||
|
||||
size_t CurrentAllocationSize = FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is similar to the std::pmr::monotonic_buffer_resource.
|
||||
*
|
||||
|
||||
@@ -9,17 +9,17 @@ using namespace ARMEmitter;
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
adr(Reg::r30, &Label);
|
||||
(void)adr(Reg::r30, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x10fffffe);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
adr(Reg::r30, &Label);
|
||||
Bind(&Label);
|
||||
(void)adr(Reg::r30, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x1000003e);
|
||||
@@ -27,17 +27,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
adr(Reg::r30, &Label);
|
||||
(void)adr(Reg::r30, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x10fffffe);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
adr(Reg::r30, &Label);
|
||||
Bind(&Label);
|
||||
(void)adr(Reg::r30, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x1000003e);
|
||||
@@ -45,42 +45,42 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
adrp(Reg::r30, &Label);
|
||||
(void)adrp(Reg::r30, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x9000001e);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
adrp(Reg::r30, &Label);
|
||||
(void)adrp(Reg::r30, &Label);
|
||||
// Move label a page away
|
||||
for (size_t i = 0; i < 1023; ++i) {
|
||||
nop();
|
||||
}
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000001e);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
adrp(Reg::r30, &Label);
|
||||
(void)adrp(Reg::r30, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x9000001e);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
adrp(Reg::r30, &Label);
|
||||
(void)adrp(Reg::r30, &Label);
|
||||
// Move label a page away
|
||||
for (size_t i = 0; i < 1023; ++i) {
|
||||
nop();
|
||||
}
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000001e);
|
||||
}
|
||||
@@ -88,17 +88,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate adr.
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
CHECK(DisassembleEncoding(1) == 0x10fffffe);
|
||||
}
|
||||
{
|
||||
// Will generate nop + adr.
|
||||
ForwardLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
Bind(&Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
@@ -107,9 +107,9 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate adr.
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x10fffffe);
|
||||
}
|
||||
@@ -117,8 +117,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate nop + adr.
|
||||
BiDirectionalLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
Bind(&Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
@@ -128,7 +128,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate adrp.
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
// Move adrp 1MB away.
|
||||
@@ -136,7 +136,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
nop();
|
||||
}
|
||||
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
nop();
|
||||
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
|
||||
CHECK(DisassembleEncoding(262146) == 0xd503201f);
|
||||
@@ -145,14 +145,14 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate nop + adrp.
|
||||
ForwardLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, and then aligned to a page.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
@@ -162,14 +162,14 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate adrp + add.
|
||||
ForwardLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, plus one instruction.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000081e);
|
||||
@@ -180,7 +180,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate adrp.
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
// Move adrp 1MB away.
|
||||
@@ -188,7 +188,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
nop();
|
||||
}
|
||||
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
nop();
|
||||
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
|
||||
CHECK(DisassembleEncoding(262146) == 0xd503201f);
|
||||
@@ -197,14 +197,14 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate nop + adrp.
|
||||
BiDirectionalLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, and then aligned to a page.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
@@ -214,14 +214,14 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
{
|
||||
// Will generate adrp + add.
|
||||
BiDirectionalLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
(void)LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, plus one instruction.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000081e);
|
||||
|
||||
@@ -9,17 +9,17 @@ using namespace ARMEmitter;
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Conditional branch immediate") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
b(Condition::CC_PL, &Label);
|
||||
(void)b(Condition::CC_PL, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x54ffffe5);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
b(Condition::CC_PL, &Label);
|
||||
Bind(&Label);
|
||||
(void)b(Condition::CC_PL, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x54000025);
|
||||
@@ -27,17 +27,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Conditional branch immediat
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
b(Condition::CC_PL, &Label);
|
||||
(void)b(Condition::CC_PL, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x54ffffe5);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
b(Condition::CC_PL, &Label);
|
||||
Bind(&Label);
|
||||
(void)b(Condition::CC_PL, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x54000025);
|
||||
@@ -46,17 +46,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Conditional branch immediat
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Branch consistent conditional") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
bc(Condition::CC_PL, &Label);
|
||||
(void)bc(Condition::CC_PL, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x54fffff5);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
bc(Condition::CC_PL, &Label);
|
||||
Bind(&Label);
|
||||
(void)bc(Condition::CC_PL, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x54000035);
|
||||
@@ -64,17 +64,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Branch consistent condition
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
bc(Condition::CC_PL, &Label);
|
||||
(void)bc(Condition::CC_PL, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x54fffff5);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
bc(Condition::CC_PL, &Label);
|
||||
Bind(&Label);
|
||||
(void)bc(Condition::CC_PL, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x54000035);
|
||||
@@ -89,17 +89,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch regist
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immediate") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
b(&Label);
|
||||
(void)b(&Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x17ffffff);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
b(&Label);
|
||||
Bind(&Label);
|
||||
(void)b(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x14000001);
|
||||
@@ -107,17 +107,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
b(&Label);
|
||||
(void)b(&Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x17ffffff);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
b(&Label);
|
||||
Bind(&Label);
|
||||
(void)b(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x14000001);
|
||||
@@ -125,17 +125,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
bl(&Label);
|
||||
(void)bl(&Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x97ffffff);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
bl(&Label);
|
||||
Bind(&Label);
|
||||
(void)bl(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x94000001);
|
||||
@@ -143,17 +143,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
bl(&Label);
|
||||
(void)bl(&Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x97ffffff);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
bl(&Label);
|
||||
Bind(&Label);
|
||||
(void)bl(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x94000001);
|
||||
@@ -162,17 +162,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Unconditional branch immedi
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x34fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3400003d);
|
||||
@@ -180,17 +180,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x34fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3400003d);
|
||||
@@ -198,17 +198,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb4fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb400003d);
|
||||
@@ -216,17 +216,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb4fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb400003d);
|
||||
@@ -234,17 +234,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x35fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3500003d);
|
||||
@@ -252,17 +252,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x35fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbnz(Size::i32Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3500003d);
|
||||
@@ -270,17 +270,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb5fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb500003d);
|
||||
@@ -288,17 +288,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb5fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
Bind(&Label);
|
||||
(void)cbnz(Size::i64Bit, Reg::r29, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb500003d);
|
||||
@@ -307,17 +307,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Compare and branch") {
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbz(Reg::r29, 0, &Label);
|
||||
(void)tbz(Reg::r29, 0, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x3607fffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
tbz(Reg::r29, 0, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbz(Reg::r29, 0, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3600003d);
|
||||
@@ -325,17 +325,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbz(Reg::r29, 0, &Label);
|
||||
(void)tbz(Reg::r29, 0, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x3607fffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
tbz(Reg::r29, 0, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbz(Reg::r29, 0, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3600003d);
|
||||
@@ -343,17 +343,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbz(Reg::r29, 63, &Label);
|
||||
(void)tbz(Reg::r29, 63, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb6fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
tbz(Reg::r29, 63, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbz(Reg::r29, 63, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb6f8003d);
|
||||
@@ -361,17 +361,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbz(Reg::r29, 63, &Label);
|
||||
(void)tbz(Reg::r29, 63, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb6fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
tbz(Reg::r29, 63, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbz(Reg::r29, 63, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb6f8003d);
|
||||
@@ -379,17 +379,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbnz(Reg::r29, 0, &Label);
|
||||
(void)tbnz(Reg::r29, 0, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x3707fffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
tbnz(Reg::r29, 0, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbnz(Reg::r29, 0, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3700003d);
|
||||
@@ -397,17 +397,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbnz(Reg::r29, 0, &Label);
|
||||
(void)tbnz(Reg::r29, 0, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x3707fffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
tbnz(Reg::r29, 0, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbnz(Reg::r29, 0, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x3700003d);
|
||||
@@ -415,17 +415,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbnz(Reg::r29, 63, &Label);
|
||||
(void)tbnz(Reg::r29, 63, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb7fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
ForwardLabel Label;
|
||||
tbnz(Reg::r29, 63, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbnz(Reg::r29, 63, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb7f8003d);
|
||||
@@ -433,17 +433,17 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Branch: Test and branch immediate")
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
tbnz(Reg::r29, 63, &Label);
|
||||
(void)tbnz(Reg::r29, 63, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0xb7fffffd);
|
||||
}
|
||||
|
||||
{
|
||||
BiDirectionalLabel Label;
|
||||
tbnz(Reg::r29, 63, &Label);
|
||||
Bind(&Label);
|
||||
(void)tbnz(Reg::r29, 63, &Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb7f8003d);
|
||||
|
||||
@@ -1323,7 +1323,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: LDAPR/STLR unscaled imme
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal") {
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
ldr(WReg::w30, &Label);
|
||||
|
||||
@@ -1332,7 +1332,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
ldr(SReg::s30, &Label);
|
||||
|
||||
@@ -1341,7 +1341,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
ldr(XReg::x30, &Label);
|
||||
|
||||
@@ -1350,7 +1350,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
ldr(DReg::d30, &Label);
|
||||
|
||||
@@ -1359,7 +1359,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
ldrsw(XReg::x30, &Label);
|
||||
|
||||
@@ -1368,7 +1368,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
ldr(QReg::q30, &Label);
|
||||
|
||||
@@ -1377,7 +1377,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
|
||||
{
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
prfm(Prefetch::PLDL1KEEP, &Label);
|
||||
|
||||
@@ -1387,7 +1387,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
ldr(WReg::w30, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x1800003e);
|
||||
@@ -1396,7 +1396,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
ldr(SReg::s30, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x1c00003e);
|
||||
@@ -1405,7 +1405,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
ldr(XReg::x30, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x5800003e);
|
||||
@@ -1414,7 +1414,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
ldr(DReg::d30, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x5c00003e);
|
||||
@@ -1423,7 +1423,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
ldrsw(XReg::x30, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x9800003e);
|
||||
@@ -1432,7 +1432,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
ldr(QReg::q30, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0x9c00003e);
|
||||
@@ -1441,7 +1441,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Load register literal")
|
||||
{
|
||||
ForwardLabel Label;
|
||||
prfm(Prefetch::PLDL1KEEP, &Label);
|
||||
Bind(&Label);
|
||||
(void)Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd8000020);
|
||||
|
||||
@@ -81,11 +81,7 @@ def IsSupportedDistro():
|
||||
|
||||
# We only support Ubuntu
|
||||
if Distro[0] == "ubuntu":
|
||||
# We only support what is available in ppa:fex-emu/fex
|
||||
return Distro[1] == "22.04" or \
|
||||
Distro[1] == "24.04" or \
|
||||
Distro[1] == "24.10" or \
|
||||
Distro[1] == "25.04"
|
||||
return Distro[1] in {"22.04", "24.04", "24.10", "25.04", "25.10"}
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@@ -277,15 +277,16 @@ void EnvLoader::Load() {
|
||||
#include <FEXCore/Config/ConfigOptions.inl>
|
||||
|
||||
if (Value) {
|
||||
EnvMap.insert_or_assign(Key, *Value);
|
||||
EnvMap.insert_or_assign(Key, std::move(*Value));
|
||||
} else {
|
||||
EnvMap.insert_or_assign(Key, Value_View);
|
||||
}
|
||||
}
|
||||
|
||||
auto GetVar = [](EnvMapType& EnvMap, const std::string_view id) -> std::optional<std::string_view> {
|
||||
if (EnvMap.find(id) != EnvMap.end()) {
|
||||
return EnvMap.at(id);
|
||||
auto GetVar = [](const EnvMapType& EnvMap, std::string_view id) -> std::optional<std::string_view> {
|
||||
const auto EnvEntry = EnvMap.find(id);
|
||||
if (EnvEntry != EnvMap.end()) {
|
||||
return EnvEntry->second;
|
||||
}
|
||||
|
||||
// If envp[] was empty, search using std::getenv()
|
||||
@@ -570,7 +571,7 @@ fextl::string GetConfigDirectory(bool Global, const PortableInformation& Portabl
|
||||
return fextl::fmt::format("{}/fex-emu/", PortableInfo.InterpreterPath);
|
||||
} else if (PortableInfo.IsPortable && ConfigOverride && !Global) {
|
||||
fextl::string AppConfigStr = ConfigOverride;
|
||||
if (PortableInfo.IsPortable && FHU::Filesystem::IsRelative(AppConfigStr)) {
|
||||
if (FHU::Filesystem::IsRelative(AppConfigStr)) {
|
||||
AppConfigStr = PortableInfo.InterpreterPath + AppConfigStr;
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ fextl::string GetServerMountFolder() {
|
||||
fextl::string GetServerSocketName() {
|
||||
FEX_CONFIG_OPT(ServerSocketPath, SERVERSOCKETPATH);
|
||||
if (ServerSocketPath().empty()) {
|
||||
return fextl::fmt::format("{}.FEXServer.Socket", ::geteuid());
|
||||
return fextl::fmt::format("{}.FEXServer.Socket", ::getuid());
|
||||
}
|
||||
return ServerSocketPath;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ fextl::string GetServerSocketPath() {
|
||||
auto Folder = GetTempFolder();
|
||||
|
||||
if (name.empty()) {
|
||||
return fextl::fmt::format("{}/{}.FEXServer.Socket", Folder, ::geteuid());
|
||||
return fextl::fmt::format("{}/{}.FEXServer.Socket", Folder, ::getuid());
|
||||
} else {
|
||||
return fextl::fmt::format("{}/{}", Folder, name);
|
||||
}
|
||||
@@ -300,6 +300,14 @@ int ConnectToAndStartServer(std::string_view InterpreterPath) {
|
||||
while (poll(&PollFD, 1, -1) == -1 && errno == EINTR)
|
||||
;
|
||||
|
||||
// Check if child signaled an error
|
||||
uint64_t error = 0;
|
||||
ssize_t bytes_read = read(fds[0], &error, sizeof(error));
|
||||
close(fds[0]);
|
||||
if (bytes_read > 0 && error != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
ServerFD = ConnectToServer(ConnectionOption::Default);
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/TypeDefines.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
/**
|
||||
* Infers the base virtual address from a file mapping (as described by parameters to a single
|
||||
* call to mmap()).
|
||||
*
|
||||
* The file offset of any given mapping need not match its virtual address offset from the base
|
||||
* mapping (file offset = 0). Instead, this function searches the corresponding ELF program headers
|
||||
* for an entry that generated the given file mapping.
|
||||
*/
|
||||
inline std::optional<uint64_t>
|
||||
InferMappingBaseAddress(std::span<const Elf64_Phdr> ProgramHeaders, uint64_t Addr, uint64_t Size, uint64_t FileOffset, int AccessFlags) {
|
||||
for (auto& phdr : ProgramHeaders) {
|
||||
if (phdr.p_type != PT_LOAD) {
|
||||
// Skip headers that don't trigger memory mappings
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((phdr.p_flags & (PF_X | PF_W | PF_R)) != (AccessFlags & (PF_X | PF_W | PF_R))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The mapped file offset must be included at the start of the section header
|
||||
auto SegmentStartOffset = phdr.p_offset - (phdr.p_vaddr & 0xfff);
|
||||
if (FileOffset >= SegmentStartOffset && FileOffset < SegmentStartOffset + phdr.p_filesz &&
|
||||
(FileOffset & Utils::FEX_PAGE_MASK) == (phdr.p_offset & Utils::FEX_PAGE_MASK)) {
|
||||
// Compute VA offset relative to the base mapping
|
||||
return Addr - (phdr.p_vaddr - (phdr.p_offset & 0xfff)) + (ProgramHeaders[0].p_vaddr - (ProgramHeaders[0].p_offset & 0xfff)) -
|
||||
(FileOffset - SegmentStartOffset);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace FEXCore
|
||||
@@ -598,16 +598,6 @@ FEXCore::HostFeatures FetchHostFeatures(FEX::CPUFeatures& Features, bool Support
|
||||
#endif
|
||||
HostFeatures.SupportsPreserveAllABI = FEX_HAS_PRESERVE_ALL_ATTR;
|
||||
|
||||
if (!Is64BitMode()) {
|
||||
///< Always disable AVX and AVX2 in 32-bit mode.
|
||||
// When AVX256 is enabled, signal frames start using significantly more stack space.
|
||||
// - 16bytes * 16 registers = 256 bytes for XMM registers.
|
||||
// - 32bytes * 16 registers = 512 bytes for YMM registers.
|
||||
// There are known game failures on real x86 hardware where a 32-bit game is running up against the wall on stack space on non-AVX
|
||||
// hardware and then explodes when run on AVX hardware. This is to guard against that.
|
||||
HostFeatures.SupportsAVX = false;
|
||||
}
|
||||
|
||||
OverrideFeatures(&HostFeatures, ForceSVEWidth());
|
||||
return HostFeatures;
|
||||
}
|
||||
|
||||
+11
-14
@@ -2,22 +2,19 @@
|
||||
#include "Common/JSONPool.h"
|
||||
|
||||
namespace FEX::JSON {
|
||||
json_t* PoolInit(jsonPool_t* Pool);
|
||||
json_t* PoolAlloc(jsonPool_t* Pool);
|
||||
static json_t* PoolInit(jsonPool_t* Pool) {
|
||||
auto* alloc = static_cast<JsonAllocator*>(Pool);
|
||||
return &*alloc->json_objects.emplace(alloc->json_objects.end());
|
||||
}
|
||||
|
||||
static json_t* PoolAlloc(jsonPool_t* Pool) {
|
||||
auto* alloc = static_cast<JsonAllocator*>(Pool);
|
||||
return &*alloc->json_objects.emplace(alloc->json_objects.end());
|
||||
}
|
||||
|
||||
JsonAllocator::JsonAllocator()
|
||||
: jsonPool_t {
|
||||
.init = FEX::JSON::PoolInit,
|
||||
.alloc = FEX::JSON::PoolAlloc,
|
||||
.init = PoolInit,
|
||||
.alloc = PoolAlloc,
|
||||
} {}
|
||||
|
||||
json_t* PoolInit(jsonPool_t* Pool) {
|
||||
JsonAllocator* alloc = static_cast<JsonAllocator*>(Pool);
|
||||
return &*alloc->json_objects.emplace(alloc->json_objects.end());
|
||||
}
|
||||
|
||||
json_t* PoolAlloc(jsonPool_t* Pool) {
|
||||
JsonAllocator* alloc = static_cast<JsonAllocator*>(Pool);
|
||||
return &*alloc->json_objects.emplace(alloc->json_objects.end());
|
||||
}
|
||||
} // namespace FEX::JSON
|
||||
@@ -13,10 +13,11 @@ void StatAllocBase::SaveHeader(FEXCore::SHMStats::AppType AppType) {
|
||||
Head = reinterpret_cast<FEXCore::SHMStats::ThreadStatsHeader*>(Base);
|
||||
Head->Size.store(CurrentSize, std::memory_order_relaxed);
|
||||
Head->Version = FEXCore::SHMStats::STATS_VERSION;
|
||||
Head->app_type = AppType;
|
||||
Head->ThreadStatsSize = sizeof(FEXCore::SHMStats::ThreadStats);
|
||||
|
||||
std::string_view GitString = GIT_DESCRIBE_STRING;
|
||||
strncpy(Head->fex_version, GitString.data(), std::min(GitString.size(), sizeof(Head->fex_version)));
|
||||
Head->app_type = AppType;
|
||||
|
||||
Stats = reinterpret_cast<FEXCore::SHMStats::ThreadStats*>(reinterpret_cast<uint64_t>(Base) + sizeof(FEXCore::SHMStats::ThreadStatsHeader));
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ public:
|
||||
|
||||
{
|
||||
auto CodeInvalidationlk = FEXCore::GuardSignalDeferringSection(CTX->GetCodeInvalidationMutex(), Thread);
|
||||
FEXCore::Context::InvalidatedEntryAccumulator Accumulator;
|
||||
CTX->InvalidateGuestCodeRange(Thread, Accumulator, reinterpret_cast<uint64_t>(CodeStart), MAX_CODE_SIZE);
|
||||
CTX->InvalidateCodeBuffersCodeRange(reinterpret_cast<uint64_t>(CodeStart), MAX_CODE_SIZE);
|
||||
CTX->InvalidateThreadCachedCodeRange(Thread, reinterpret_cast<uint64_t>(CodeStart), MAX_CODE_SIZE);
|
||||
}
|
||||
|
||||
ClearStats();
|
||||
@@ -447,18 +447,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
FEXCore::HLE::SyscallABI GetSyscallABI(uint64_t Syscall) override {
|
||||
if (Syscall == 0) {
|
||||
// Claim syscall 0 is simple for instcountci inline tests.
|
||||
return FEXCore::HLE::SyscallABI {
|
||||
.NumArgs = 0,
|
||||
.HasReturn = true,
|
||||
.HostSyscallNumber = 0, // Just map to host syscall zero, it isn't going to get called.
|
||||
};
|
||||
}
|
||||
return {0, false, -1};
|
||||
}
|
||||
|
||||
// These are no-ops implementations of the SyscallHandler API
|
||||
std::optional<FEXCore::ExecutableFileSectionInfo>
|
||||
LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) override {
|
||||
|
||||
@@ -16,11 +16,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
FEXCore::HLE::SyscallABI GetSyscallABI(uint64_t Syscall) override {
|
||||
// Don't do anything
|
||||
return {0, false, 0};
|
||||
}
|
||||
|
||||
// These are no-ops implementations of the SyscallHandler API
|
||||
std::optional<FEXCore::ExecutableFileSectionInfo> LookupExecutableFileSection(FEXCore::Core::InternalThreadState&, uint64_t) override {
|
||||
return std::nullopt;
|
||||
|
||||
@@ -209,7 +209,7 @@ bool ELFContainer::LoadELF_32() {
|
||||
DynamicProgram = Header._32.e_type != ET_EXEC;
|
||||
|
||||
// Default BRK size
|
||||
BRKSize = 4096;
|
||||
BRKSize = FEXCore::Utils::FEX_PAGE_SIZE;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -344,7 +344,7 @@ void ELFContainer::CalculateMemoryLayouts() {
|
||||
}
|
||||
|
||||
// Calculate BRK
|
||||
MaxPhysAddr = FEXCore::AlignUp(MaxPhysAddr, 4096);
|
||||
MaxPhysAddr = FEXCore::AlignUp(MaxPhysAddr, FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
BRKBase = MaxPhysAddr;
|
||||
MaxPhysAddr += BRKSize;
|
||||
|
||||
|
||||
@@ -182,17 +182,17 @@ struct ELFParser {
|
||||
} else {
|
||||
phdrs.resize(ehdr.e_phnum);
|
||||
|
||||
if (pread(fd, &phdrs[0], sizeof(Elf64_Phdr) * ehdr.e_phnum, ehdr.e_phoff) == -1) {
|
||||
if (pread(fd, phdrs.data(), sizeof(Elf64_Phdr) * ehdr.e_phnum, ehdr.e_phoff) == -1) {
|
||||
LogMan::Msg::EFmt("Failed to read phdr64 from '{}'", fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto phdr : phdrs) {
|
||||
for (const auto& phdr : phdrs) {
|
||||
if (phdr.p_type == PT_INTERP) {
|
||||
InterpreterElf.resize(phdr.p_filesz);
|
||||
|
||||
if (pread(fd, &InterpreterElf[0], phdr.p_filesz, phdr.p_offset) == -1) {
|
||||
if (pread(fd, InterpreterElf.data(), phdr.p_filesz, phdr.p_offset) == -1) {
|
||||
LogMan::Msg::EFmt("Failed to read interpreter from '{}'", fd);
|
||||
return false;
|
||||
}
|
||||
@@ -202,10 +202,9 @@ struct ELFParser {
|
||||
return true;
|
||||
}
|
||||
|
||||
ptrdiff_t FileToVA(off_t FileOffset) {
|
||||
for (auto phdr : phdrs) {
|
||||
ptrdiff_t FileToVA(off_t FileOffset) const {
|
||||
for (const auto& phdr : phdrs) {
|
||||
if (phdr.p_offset <= FileOffset && (phdr.p_offset + phdr.p_filesz) > FileOffset) {
|
||||
|
||||
auto SectionFileOffset = FileOffset - phdr.p_offset;
|
||||
|
||||
if (SectionFileOffset < phdr.p_memsz) {
|
||||
@@ -217,10 +216,9 @@ struct ELFParser {
|
||||
return {};
|
||||
}
|
||||
|
||||
off_t VAToFile(ptrdiff_t VAOffset) {
|
||||
for (auto phdr : phdrs) {
|
||||
off_t VAToFile(ptrdiff_t VAOffset) const {
|
||||
for (const auto& phdr : phdrs) {
|
||||
if (phdr.p_vaddr <= VAOffset && (phdr.p_vaddr + phdr.p_memsz) > VAOffset) {
|
||||
|
||||
auto SectionVAOffset = VAOffset - phdr.p_vaddr;
|
||||
|
||||
if (SectionVAOffset < phdr.p_filesz) {
|
||||
|
||||
@@ -507,13 +507,8 @@ ApplicationWindow {
|
||||
|
||||
ButtonGroup {
|
||||
id: tsoButtonGroup
|
||||
buttons: [tso1, tso2, tso3]
|
||||
// Trying to be too clever here will trigger property binding loops,
|
||||
// so require both TSOEnabled and ParanoidTSO to be listed in the config.
|
||||
// If they are not, the state will be displayed as undetermined.
|
||||
checkedButton: !(ConfigModel.has("TSOEnabled", refreshCache) && (ConfigModel.has("ParanoidTSO", refreshCache))) ? null
|
||||
: ConfigModel.getBool("ParanoidTSO", refreshCache) ? tso3
|
||||
: ConfigModel.getBool("TSOEnabled", refreshCache) ? tso2 : tso1
|
||||
buttons: [tso1, tso2]
|
||||
checkedButton: ConfigModel.getBool("TSOEnabled", refreshCache) ? tso2 : tso1
|
||||
|
||||
property int pendingItemChange: -1
|
||||
|
||||
@@ -524,8 +519,6 @@ ApplicationWindow {
|
||||
|
||||
var newIndex = pendingItemChange
|
||||
var TSOEnabled = newIndex === 1
|
||||
var ParanoidTSO = newIndex === 2
|
||||
ConfigModel.setBool("ParanoidTSO", ParanoidTSO)
|
||||
ConfigModel.setBool("TSOEnabled", TSOEnabled)
|
||||
|
||||
pendingItemChange = -1;
|
||||
@@ -543,8 +536,6 @@ ApplicationWindow {
|
||||
|
||||
var newIndex = pendingItemChange
|
||||
var TSOEnabled = newIndex === 1
|
||||
var ParanoidTSO = newIndex === 2
|
||||
ConfigModel.setBool("ParanoidTSO", ParanoidTSO)
|
||||
ConfigModel.setBool("TSOEnabled", TSOEnabled)
|
||||
|
||||
pendingItemChange = -1;
|
||||
@@ -585,12 +576,6 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
id: tso3
|
||||
text: qsTr("Overly accurate (paranoid TSO)")
|
||||
onToggled: tsoButtonGroup.onClickedButton(2)
|
||||
}
|
||||
}
|
||||
|
||||
ConfigCheckBox {
|
||||
@@ -721,17 +706,10 @@ ApplicationWindow {
|
||||
}
|
||||
|
||||
ConfigCheckBox {
|
||||
id: x87ReducedPrecisionCheckbox
|
||||
text: qsTr("Reduced x87 precision")
|
||||
config: "X87ReducedPrecision"
|
||||
}
|
||||
|
||||
ConfigCheckBox {
|
||||
text: qsTr("Strict reduced x87 precision")
|
||||
config: "X87StrictReducedPrecision"
|
||||
enabled: x87ReducedPrecisionCheckbox.checked
|
||||
}
|
||||
|
||||
ConfigCheckBox {
|
||||
text: qsTr("Unsafe local flags optimization")
|
||||
config: "ABILocalFlags"
|
||||
|
||||
@@ -222,6 +222,28 @@ void CheckForGCS() {
|
||||
}
|
||||
} // namespace FEX::GCS
|
||||
|
||||
namespace FEX::UnalignedAtomic {
|
||||
void SetupKernelUnalignedAtomics() {
|
||||
#ifndef PR_ARM64_SET_UNALIGN_ATOMIC
|
||||
#define PR_ARM64_SET_UNALIGN_ATOMIC 0x46455849
|
||||
#define PR_ARM64_UNALIGN_ATOMIC_EMULATE (1UL << 0)
|
||||
#define PR_ARM64_UNALIGN_ATOMIC_BACKPATCH (1UL << 1)
|
||||
#define PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1UL << 2)
|
||||
#endif
|
||||
|
||||
// Interfaces with downstream FEX kernel patches to control unaligned atomic handling
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS);
|
||||
|
||||
uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) |
|
||||
(ParanoidTSO() ? 0 : PR_ARM64_UNALIGN_ATOMIC_BACKPATCH) | PR_ARM64_UNALIGN_ATOMIC_EMULATE;
|
||||
|
||||
if (prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0) != -1) {
|
||||
LogMan::Msg::IFmt("FEX: Kernel unaligned atomics enabled!");
|
||||
}
|
||||
}
|
||||
} // namespace FEX::UnalignedAtomic
|
||||
|
||||
/**
|
||||
* @brief Get an FD from an environment variable and then unset the environment variable.
|
||||
*
|
||||
@@ -458,6 +480,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
|
||||
// Setup TSO hardware emulation immediately after initializing the context.
|
||||
FEX::TSO::SetupTSOEmulation(CTX.get());
|
||||
FEX::UnalignedAtomic::SetupKernelUnalignedAtomics();
|
||||
|
||||
if (!Loader.Is64BitMode()) {
|
||||
// Tell the kernel we want to use the compat input syscalls even though we're
|
||||
|
||||
@@ -123,14 +123,14 @@ void FileManager::LoadThunkDatabase(fextl::unordered_map<fextl::string, ThunkDBO
|
||||
} else if (ItemName == "Depends") {
|
||||
jsonType_t PropertyType = json_getType(LibraryItem);
|
||||
if (PropertyType == JSON_TEXT) {
|
||||
DBObject->second.Depends.insert(json_getValue(LibraryItem));
|
||||
DBObject->second.Depends.emplace(json_getValue(LibraryItem));
|
||||
} else if (PropertyType == JSON_ARRAY) {
|
||||
for (const json_t* Depend = json_getChild(LibraryItem); Depend != nullptr; Depend = json_getSibling(Depend)) {
|
||||
DBObject->second.Depends.insert(json_getValue(Depend));
|
||||
DBObject->second.Depends.emplace(json_getValue(Depend));
|
||||
}
|
||||
}
|
||||
} else if (ItemName == "Overlay") {
|
||||
auto AddWithReplacement = [HomeDirectory, &PathPrefixes](ThunkDBObject& DBObject, fextl::string LibraryItem) {
|
||||
auto AddWithReplacement = [HomeDirectory, &PathPrefixes](ThunkDBObject& DBObject, std::string_view LibraryItem) {
|
||||
// Walk through template string and fill in prefixes from right to left
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
@@ -144,8 +144,8 @@ void FileManager::LoadThunkDatabase(fextl::unordered_map<fextl::string, ThunkDBO
|
||||
// Sort offsets in descending order to enable safe in-place replacement
|
||||
std::sort(std::begin(PrefixPositions), std::end(PrefixPositions), std::greater<> {});
|
||||
|
||||
for (auto& LibPrefix : PathPrefixes) {
|
||||
fextl::string Replacement = LibraryItem;
|
||||
for (const auto& LibPrefix : PathPrefixes) {
|
||||
fextl::string Replacement(LibraryItem);
|
||||
for (auto PrefixPos : PrefixPositions) {
|
||||
if (PrefixPos == fextl::string::npos) {
|
||||
continue;
|
||||
|
||||
@@ -6,19 +6,59 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "LinuxSyscalls/Seccomp/BPFEmitter.h"
|
||||
#include "LinuxSyscalls/Seccomp/SeccompEmulator.h"
|
||||
|
||||
#include <FEXCore/Utils/AllocatorHooks.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <linux/bpf_common.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/seccomp.h>
|
||||
|
||||
namespace FEX::HLE {
|
||||
|
||||
#define EMIT_INST(x) \
|
||||
do { \
|
||||
if constexpr (CalculateSize) { \
|
||||
OpSize += 4; \
|
||||
} else { \
|
||||
x; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_ERROR(x) \
|
||||
if constexpr (CalculateSize) { \
|
||||
return ~0ULL; \
|
||||
} else { \
|
||||
static_assert(x == -EINVAL, "Early return error evaluation only supports EINVAL"); \
|
||||
return x; \
|
||||
}
|
||||
|
||||
#define RETURN_SUCCESS() \
|
||||
do { \
|
||||
if constexpr (CalculateSize) { \
|
||||
return OpSize; \
|
||||
} else { \
|
||||
return 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VALIDATE(cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
RETURN_ERROR(-EINVAL) \
|
||||
} \
|
||||
} while (0)
|
||||
namespace FEX::HLE {
|
||||
|
||||
using SizeErrorCheck = decltype([](uint64_t Result) -> bool { return Result == ~0ULL; });
|
||||
using EmissionErrorCheck = decltype([](uint64_t Result) { return Result != 0; });
|
||||
|
||||
// Register selection comes from function signature.
|
||||
constexpr auto REG_A = ARMEmitter::WReg::w0;
|
||||
constexpr auto REG_X = ARMEmitter::WReg::w1;
|
||||
constexpr auto REG_TMP = ARMEmitter::WReg::w2;
|
||||
constexpr auto REG_TMP2 = ARMEmitter::WReg::w3;
|
||||
constexpr auto REG_SECCOMP_DATA = ARMEmitter::XReg::x4;
|
||||
|
||||
template<bool CalculateSize>
|
||||
uint64_t BPFEmitter::HandleLoad(uint32_t BPFIP, const sock_filter* Inst) {
|
||||
VALIDATE(BPF_SIZE(Inst->code) == BPF_W);
|
||||
@@ -158,13 +198,13 @@ uint64_t BPFEmitter::HandleJmp(uint32_t BPFIP, uint32_t NumInst, const sock_filt
|
||||
// Must not jump past the end.
|
||||
VALIDATE(Target < NumInst);
|
||||
|
||||
fextl::unordered_map<uint32_t, ARMEmitter::ForwardLabel>::iterator TargetLabel {};
|
||||
JumpLabelIterator TargetLabel {};
|
||||
|
||||
if constexpr (!CalculateSize) {
|
||||
TargetLabel = JumpLabels.try_emplace(Target, ARMEmitter::ForwardLabel {}).first;
|
||||
}
|
||||
|
||||
EMIT_INST(b(&TargetLabel->second));
|
||||
EMIT_INST((void)b(&TargetLabel->second));
|
||||
break;
|
||||
}
|
||||
case BPF_JEQ:
|
||||
@@ -200,16 +240,16 @@ uint64_t BPFEmitter::HandleJmp(uint32_t BPFIP, uint32_t NumInst, const sock_filt
|
||||
RETURN_ERROR(-EINVAL);
|
||||
}
|
||||
|
||||
fextl::unordered_map<uint32_t, ARMEmitter::ForwardLabel>::iterator TargetTrueLabel {};
|
||||
fextl::unordered_map<uint32_t, ARMEmitter::ForwardLabel>::iterator TargetFalseLabel {};
|
||||
JumpLabelIterator TargetTrueLabel {};
|
||||
JumpLabelIterator TargetFalseLabel {};
|
||||
|
||||
if constexpr (!CalculateSize) {
|
||||
TargetTrueLabel = JumpLabels.try_emplace(TargetTrue, ARMEmitter::ForwardLabel {}).first;
|
||||
TargetFalseLabel = JumpLabels.try_emplace(TargetFalse, ARMEmitter::ForwardLabel {}).first;
|
||||
}
|
||||
|
||||
EMIT_INST(b(CompareResultOp, &TargetTrueLabel->second));
|
||||
EMIT_INST(b(&TargetFalseLabel->second));
|
||||
EMIT_INST((void)b(CompareResultOp, &TargetTrueLabel->second));
|
||||
EMIT_INST((void)b(&TargetFalseLabel->second));
|
||||
break;
|
||||
}
|
||||
default: RETURN_ERROR(-EINVAL); // Unknown jump type
|
||||
@@ -263,7 +303,7 @@ uint64_t BPFEmitter::HandleEmission(uint32_t flags, const sock_fprog* prog) {
|
||||
if constexpr (!CalculateSize) {
|
||||
auto jump_label = JumpLabels.find(i);
|
||||
if (jump_label != JumpLabels.end()) {
|
||||
Bind(&jump_label->second);
|
||||
(void)Bind(&jump_label->second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,6 +384,8 @@ uint64_t BPFEmitter::JITFilter(uint32_t flags, const sock_fprog* prog) {
|
||||
|
||||
SetBuffer((uint8_t*)FEXCore::Allocator::mmap(nullptr, FuncSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), FuncSize);
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(GetBufferBase()), FuncSize);
|
||||
|
||||
const auto CodeBegin = GetCursorAddress<uint8_t*>();
|
||||
|
||||
uint64_t Result = HandleEmission<false, EmissionErrorCheck>(flags, prog);
|
||||
@@ -359,7 +401,7 @@ uint64_t BPFEmitter::JITFilter(uint32_t flags, const sock_fprog* prog) {
|
||||
// Emit the constant pool.
|
||||
Align();
|
||||
for (auto& Const : ConstPool) {
|
||||
Bind(&Const.second);
|
||||
(void)Bind(&Const.second);
|
||||
dc32(Const.first);
|
||||
}
|
||||
|
||||
@@ -369,7 +411,7 @@ uint64_t BPFEmitter::JITFilter(uint32_t flags, const sock_fprog* prog) {
|
||||
|
||||
if constexpr (false) {
|
||||
// Useful for debugging seccomp filters.
|
||||
LogMan::Msg::DFmt("JITFilter: disas 0x{:x},+{}", (uint64_t)CodeBegin, CodeOnlySize);
|
||||
LogMan::Msg::DFmt("JITFilter: disas 0x{:x},+{}", fmt::ptr(CodeBegin), CodeOnlySize);
|
||||
}
|
||||
|
||||
ConstPool.clear();
|
||||
|
||||
@@ -49,47 +49,14 @@ private:
|
||||
template<bool CalculateSize>
|
||||
uint64_t HandleMisc(uint32_t BPFIP, const sock_filter* Inst);
|
||||
|
||||
#define EMIT_INST(x) \
|
||||
do { \
|
||||
if constexpr (CalculateSize) { \
|
||||
OpSize += 4; \
|
||||
} else { \
|
||||
x; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RETURN_ERROR(x) \
|
||||
if constexpr (CalculateSize) { \
|
||||
return ~0ULL; \
|
||||
} else { \
|
||||
static_assert(x == -EINVAL, "Early return error evaluation only supports EINVAL"); \
|
||||
return x; \
|
||||
}
|
||||
|
||||
#define RETURN_SUCCESS() \
|
||||
do { \
|
||||
if constexpr (CalculateSize) { \
|
||||
return OpSize; \
|
||||
} else { \
|
||||
return 0; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
using SizeErrorCheck = decltype([](uint64_t Result) -> bool { return Result == ~0ULL; });
|
||||
using EmissionErrorCheck = decltype([](uint64_t Result) { return Result != 0; });
|
||||
|
||||
template<bool CalculateSize, class Pred>
|
||||
uint64_t HandleEmission(uint32_t flags, const sock_fprog* prog);
|
||||
|
||||
// Register selection comes from function signature.
|
||||
constexpr static auto REG_A = ARMEmitter::WReg::w0;
|
||||
constexpr static auto REG_X = ARMEmitter::WReg::w1;
|
||||
constexpr static auto REG_TMP = ARMEmitter::WReg::w2;
|
||||
constexpr static auto REG_TMP2 = ARMEmitter::WReg::w3;
|
||||
constexpr static auto REG_SECCOMP_DATA = ARMEmitter::XReg::x4;
|
||||
fextl::unordered_map<uint32_t, ARMEmitter::ForwardLabel> JumpLabels;
|
||||
fextl::unordered_map<uint32_t, ARMEmitter::ForwardLabel> ConstPool;
|
||||
|
||||
using JumpLabelIterator = decltype(JumpLabels)::iterator;
|
||||
|
||||
void* Func {};
|
||||
size_t FuncSize {};
|
||||
};
|
||||
|
||||
@@ -300,6 +300,8 @@ void SeccompEmulator::DeserializeFilters(FEXCore::Core::CpuStateFrame* Frame, in
|
||||
|
||||
::mprotect(Ptr, SFilter.CodeSize, PROT_READ | PROT_EXEC);
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(Ptr), SFilter.CodeSize);
|
||||
|
||||
auto& it =
|
||||
Filters.emplace_back(SeccompFilterInfo {(SeccompFilterFunc)Ptr, 1, SFilter.CodeSize, SFilter.FilterInstructions, SFilter.ShouldLog});
|
||||
TotalFilterInstructions += SFilter.FilterInstructions;
|
||||
|
||||
@@ -579,80 +579,77 @@ void SignalDelegator::HandleGuestSignal(FEX::HLE::ThreadStateObject* ThreadObjec
|
||||
ucontext_t* _context = (ucontext_t*)UContext;
|
||||
auto SigInfo = *static_cast<siginfo_t*>(Info);
|
||||
|
||||
constexpr bool SupportDeferredSignals = true;
|
||||
if (SupportDeferredSignals) {
|
||||
auto MustDeferSignal = (Thread->CurrentFrame->State.DeferredSignalRefCount.Load() != 0);
|
||||
auto MustDeferSignal = (Thread->CurrentFrame->State.DeferredSignalRefCount.Load() != 0);
|
||||
|
||||
if (Signal == SIGSEGV && SigInfo.si_code == SEGV_ACCERR && SigInfo.si_addr == reinterpret_cast<void*>(&Thread->InterruptFaultPage)) {
|
||||
if (!MustDeferSignal) {
|
||||
// We just reached the end of the outermost signal-deferring section and faulted to check for pending signals.
|
||||
// Pull a signal frame off the stack.
|
||||
if (Signal == SIGSEGV && SigInfo.si_code == SEGV_ACCERR && SigInfo.si_addr == reinterpret_cast<void*>(&Thread->InterruptFaultPage)) {
|
||||
if (!MustDeferSignal) {
|
||||
// We just reached the end of the outermost signal-deferring section and faulted to check for pending signals.
|
||||
// Pull a signal frame off the stack.
|
||||
|
||||
mprotect(reinterpret_cast<void*>(&Thread->InterruptFaultPage), sizeof(Thread->InterruptFaultPage), PROT_READ | PROT_WRITE);
|
||||
mprotect(reinterpret_cast<void*>(&Thread->InterruptFaultPage), sizeof(Thread->InterruptFaultPage), PROT_READ | PROT_WRITE);
|
||||
|
||||
if (ThreadObject->SignalInfo.DeferredSignalFrames.empty()) {
|
||||
// No signals to defer. Just set the fault page back to RW and continue execution.
|
||||
// This occurs as a minor race condition between the refcount decrement and the access to the fault page.
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& Top = ThreadObject->SignalInfo.DeferredSignalFrames.back();
|
||||
Signal = Top.Signal;
|
||||
SigInfo = Top.Info;
|
||||
// sig mask has been updated at the defer time, recover the original mask
|
||||
memcpy(&_context->uc_sigmask, &Top.SigMask, sizeof(uint64_t));
|
||||
ThreadObject->SignalInfo.DeferredSignalFrames.pop_back();
|
||||
|
||||
// Until we re-protect the page to PROT_NONE, FEX will now *permanently* defer signals and /not/ check them.
|
||||
//
|
||||
// In order to return /back/ to a sane state, we wait for the rt_sigreturn to happen.
|
||||
// rt_sigreturn will check if there are any more deferred signals to handle
|
||||
// - If there are deferred signals
|
||||
// - mprotect back to PROT_NONE
|
||||
// - sigreturn will trampoline out to the previous fault address check, SIGSEGV and restart
|
||||
// - If there are *no* deferred signals
|
||||
// - No need to mprotect, it is already RW
|
||||
} else {
|
||||
#ifdef _M_ARM_64
|
||||
// If RefCount != 0 then that means we hit an access with nested signal-deferring sections.
|
||||
// Increment the PC past the `str zr, [x1]` to continue code execution until we reach the outermost section.
|
||||
ArchHelpers::Context::SetPc(UContext, ArchHelpers::Context::GetPc(UContext) + 4);
|
||||
if (ThreadObject->SignalInfo.DeferredSignalFrames.empty()) {
|
||||
// No signals to defer. Just set the fault page back to RW and continue execution.
|
||||
// This occurs as a minor race condition between the refcount decrement and the access to the fault page.
|
||||
return;
|
||||
#else
|
||||
// X86 should always be doing a refcount compare and branch since we can't guarantee instruction size.
|
||||
// ARM64 just always does the access to reduce branching overhead.
|
||||
ERROR_AND_DIE_FMT("X86 shouldn't hit this InterruptFaultPage");
|
||||
#endif
|
||||
}
|
||||
} else if (FaultSafeUserMemAccess::TryHandleSafeFault(Signal, SigInfo, UContext)) {
|
||||
ERROR_AND_DIE_FMT("Received invalid data to syscall. Crashing now!");
|
||||
|
||||
const auto& Top = ThreadObject->SignalInfo.DeferredSignalFrames.back();
|
||||
Signal = Top.Signal;
|
||||
SigInfo = Top.Info;
|
||||
// sig mask has been updated at the defer time, recover the original mask
|
||||
memcpy(&_context->uc_sigmask, &Top.SigMask, sizeof(uint64_t));
|
||||
ThreadObject->SignalInfo.DeferredSignalFrames.pop_back();
|
||||
|
||||
// Until we re-protect the page to PROT_NONE, FEX will now *permanently* defer signals and /not/ check them.
|
||||
//
|
||||
// In order to return /back/ to a sane state, we wait for the rt_sigreturn to happen.
|
||||
// rt_sigreturn will check if there are any more deferred signals to handle
|
||||
// - If there are deferred signals
|
||||
// - mprotect back to PROT_NONE
|
||||
// - sigreturn will trampoline out to the previous fault address check, SIGSEGV and restart
|
||||
// - If there are *no* deferred signals
|
||||
// - No need to mprotect, it is already RW
|
||||
} else {
|
||||
if (IsAsyncSignal(&SigInfo, Signal) && MustDeferSignal) {
|
||||
// If the signal is asynchronous (as determined by si_code) and FEX is in a state of needing
|
||||
// to defer the signal, then add the signal to the thread's signal queue.
|
||||
LOGMAN_THROW_A_FMT(ThreadObject->SignalInfo.DeferredSignalFrames.size() != ThreadObject->SignalInfo.DeferredSignalFrames.capacity(),
|
||||
"Deferred signals vector hit "
|
||||
"capacity size. This will "
|
||||
"likely crash! Asserting now!");
|
||||
#ifdef _M_ARM_64
|
||||
// If RefCount != 0 then that means we hit an access with nested signal-deferring sections.
|
||||
// Increment the PC past the `str zr, [x1]` to continue code execution until we reach the outermost section.
|
||||
ArchHelpers::Context::SetPc(UContext, ArchHelpers::Context::GetPc(UContext) + 4);
|
||||
return;
|
||||
#else
|
||||
// X86 should always be doing a refcount compare and branch since we can't guarantee instruction size.
|
||||
// ARM64 just always does the access to reduce branching overhead.
|
||||
ERROR_AND_DIE_FMT("X86 shouldn't hit this InterruptFaultPage");
|
||||
#endif
|
||||
}
|
||||
} else if (FaultSafeUserMemAccess::TryHandleSafeFault(Signal, SigInfo, UContext)) {
|
||||
ERROR_AND_DIE_FMT("Received invalid data to syscall. Crashing now!");
|
||||
} else {
|
||||
if (IsAsyncSignal(&SigInfo, Signal) && MustDeferSignal) {
|
||||
// If the signal is asynchronous (as determined by si_code) and FEX is in a state of needing
|
||||
// to defer the signal, then add the signal to the thread's signal queue.
|
||||
LOGMAN_THROW_A_FMT(ThreadObject->SignalInfo.DeferredSignalFrames.size() != ThreadObject->SignalInfo.DeferredSignalFrames.capacity(),
|
||||
"Deferred signals vector hit "
|
||||
"capacity size. This will "
|
||||
"likely crash! Asserting now!");
|
||||
|
||||
ThreadObject->SignalInfo.DeferredSignalFrames.emplace_back(ThreadStateObject::DeferredSignalState {
|
||||
.Info = SigInfo,
|
||||
.Signal = Signal,
|
||||
.SigMask = _context->uc_sigmask.__val[0],
|
||||
});
|
||||
ThreadObject->SignalInfo.DeferredSignalFrames.emplace_back(ThreadStateObject::DeferredSignalState {
|
||||
.Info = SigInfo,
|
||||
.Signal = Signal,
|
||||
.SigMask = _context->uc_sigmask.__val[0],
|
||||
});
|
||||
|
||||
uint64_t NewMask = GetNewSigMask(Signal);
|
||||
uint64_t NewMask = GetNewSigMask(Signal);
|
||||
|
||||
// Update our host signal mask so we don't hit race conditions with signals
|
||||
// This allows us to maintain the expected signal mask through the guest signal handling and then all the way back again
|
||||
memcpy(&_context->uc_sigmask, &NewMask, sizeof(uint64_t));
|
||||
// Update our host signal mask so we don't hit race conditions with signals
|
||||
// This allows us to maintain the expected signal mask through the guest signal handling and then all the way back again
|
||||
memcpy(&_context->uc_sigmask, &NewMask, sizeof(uint64_t));
|
||||
|
||||
// Now update the faulting page permissions so it will fault on write.
|
||||
mprotect(reinterpret_cast<void*>(&Thread->InterruptFaultPage), sizeof(Thread->InterruptFaultPage), PROT_NONE);
|
||||
// Now update the faulting page permissions so it will fault on write.
|
||||
mprotect(reinterpret_cast<void*>(&Thread->InterruptFaultPage), sizeof(Thread->InterruptFaultPage), PROT_NONE);
|
||||
|
||||
// Postpone the remainder of signal handling logic until we process the SIGSEGV triggered by writing to InterruptFaultPage.
|
||||
return;
|
||||
}
|
||||
// Postpone the remainder of signal handling logic until we process the SIGSEGV triggered by writing to InterruptFaultPage.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -873,9 +870,7 @@ SignalDelegator::SignalDelegator(FEXCore::Context::Context* _CTX, const std::str
|
||||
HostHandlers[SIGKILL].Installed = true;
|
||||
HostHandlers[SIGSTOP].Installed = true;
|
||||
|
||||
if (ParanoidTSO()) {
|
||||
UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::Paranoid;
|
||||
} else if (HalfBarrierTSOEnabled()) {
|
||||
if (HalfBarrierTSOEnabled()) {
|
||||
UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::HalfBarrier;
|
||||
} else {
|
||||
UnalignedHandlerType = FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::NonAtomic;
|
||||
@@ -995,6 +990,7 @@ void SignalDelegator::RegisterTLSState(FEX::HLE::ThreadStateObject* Thread) {
|
||||
// Set up our signal alternative stack
|
||||
// This is per thread rather than per signal
|
||||
Thread->SignalInfo.AltStackPtr = FEXCore::Allocator::mmap(nullptr, SIGSTKSZ * 16, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(Thread->SignalInfo.AltStackPtr), SIGSTKSZ * 16);
|
||||
stack_t altstack {};
|
||||
altstack.ss_sp = reinterpret_cast<void*>(reinterpret_cast<uint64_t>(Thread->SignalInfo.AltStackPtr) + 8);
|
||||
altstack.ss_size = SIGSTKSZ * 16 - 8;
|
||||
@@ -1005,7 +1001,7 @@ void SignalDelegator::RegisterTLSState(FEX::HLE::ThreadStateObject* Thread) {
|
||||
memcpy(Thread->SignalInfo.AltStackPtr, &Thread, sizeof(void*));
|
||||
|
||||
// Protect the first page of the alt-stack for overflow protection.
|
||||
mprotect(Thread->SignalInfo.AltStackPtr, 4096, PROT_READ);
|
||||
mprotect(Thread->SignalInfo.AltStackPtr, FEXCore::Utils::FEX_PAGE_SIZE, PROT_READ);
|
||||
|
||||
// Register the alt stack
|
||||
const int Result = sigaltstack(&altstack, nullptr);
|
||||
|
||||
@@ -165,7 +165,6 @@ private:
|
||||
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
const fextl::string ApplicationName;
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
FEX_CONFIG_OPT(HalfBarrierTSOEnabled, HALFBARRIERTSOENABLED);
|
||||
|
||||
FEXCore::ArchHelpers::Arm64::UnalignedHandlerType UnalignedHandlerType {FEXCore::ArchHelpers::Arm64::UnalignedHandlerType::HalfBarrier};
|
||||
|
||||
@@ -707,7 +707,7 @@ uint64_t SyscallHandler::HandleBRK(FEXCore::Core::CpuStateFrame* Frame, void* Ad
|
||||
DataSpaceMappedSize = 0;
|
||||
} else {
|
||||
uint64_t NewSize = NewEnd - DataSpace;
|
||||
uint64_t NewSizeAligned = FEXCore::AlignUp(NewSize, 4096);
|
||||
uint64_t NewSizeAligned = FEXCore::AlignUp(NewSize, FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
|
||||
if (NewSizeAligned < DataSpaceMappedSize) {
|
||||
// If we are shrinking the brk then munmap the ranges
|
||||
@@ -721,7 +721,7 @@ uint64_t SyscallHandler::HandleBRK(FEXCore::Core::CpuStateFrame* Frame, void* Ad
|
||||
|
||||
DataSpaceMappedSize = NewSizeAligned;
|
||||
} else if (NewSize > DataSpaceMappedSize) {
|
||||
uint64_t AllocateNewSize = FEXCore::AlignUp(NewSize, 4096) - DataSpaceMappedSize;
|
||||
uint64_t AllocateNewSize = FEXCore::AlignUp(NewSize, FEXCore::Utils::FEX_PAGE_SIZE) - DataSpaceMappedSize;
|
||||
if (!Is64BitMode() && (DataSpace + DataSpaceMappedSize + AllocateNewSize > 0x1'0000'0000ULL)) {
|
||||
// If we are 32bit and we tried going about the 32bit limit then out of memory
|
||||
return DataSpace + DataSpaceSize;
|
||||
|
||||
@@ -138,9 +138,7 @@ public:
|
||||
SyscallPtrArg5 Ptr5;
|
||||
SyscallPtrArg6 Ptr6;
|
||||
};
|
||||
int32_t HostSyscallNumber;
|
||||
uint8_t NumArgs;
|
||||
FEXCore::IR::SyscallFlags Flags;
|
||||
#ifdef DEBUG_STRACE
|
||||
fextl::string StraceFmt;
|
||||
#endif
|
||||
@@ -150,32 +148,14 @@ public:
|
||||
return &Definitions.at(Syscall);
|
||||
}
|
||||
|
||||
FEXCore::HLE::SyscallABI GetSyscallABI(uint64_t Syscall) override {
|
||||
if (NeedsSeccomp) {
|
||||
// Override ABI if seccomp is enabled.
|
||||
return {FEXCore::HLE::SyscallArguments::MAX_ARGS, true, -1};
|
||||
}
|
||||
auto& Def = Definitions.at(Syscall);
|
||||
return {Def.NumArgs, true, Def.HostSyscallNumber};
|
||||
}
|
||||
|
||||
FEXCore::IR::SyscallFlags GetSyscallFlags(uint64_t Syscall) const override {
|
||||
if (NeedsSeccomp) {
|
||||
// Override flags if seccomp is enabled.
|
||||
return FEXCore::IR::SyscallFlags::DEFAULT;
|
||||
}
|
||||
auto& Def = Definitions.at(Syscall);
|
||||
return Def.Flags;
|
||||
}
|
||||
|
||||
virtual void RegisterSyscall_32(int SyscallNumber, int32_t HostSyscallNumber, FEXCore::IR::SyscallFlags Flags,
|
||||
virtual void RegisterSyscall_32(int SyscallNumber,
|
||||
#ifdef DEBUG_STRACE
|
||||
const fextl::string& TraceFormatString,
|
||||
#endif
|
||||
void* SyscallHandler, int ArgumentCount) {
|
||||
}
|
||||
|
||||
virtual void RegisterSyscall_64(int SyscallNumber, int32_t HostSyscallNumber, FEXCore::IR::SyscallFlags Flags,
|
||||
virtual void RegisterSyscall_64(int SyscallNumber,
|
||||
#ifdef DEBUG_STRACE
|
||||
const fextl::string& TraceFormatString,
|
||||
#endif
|
||||
@@ -661,14 +641,8 @@ inline static uint64_t futimesat_compat(int dirfd, const char* pathname, const T
|
||||
} // namespace FEX::HLE
|
||||
|
||||
// Registers syscall for both 32bit and 64bit
|
||||
#define REGISTER_SYSCALL_IMPL(name, lambda) REGISTER_SYSCALL_IMPL_INTERNAL(name, ~0, FEXCore::IR::SyscallFlags::DEFAULT, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_FLAGS(name, flags, lambda) REGISTER_SYSCALL_IMPL_INTERNAL(name, ~0, flags, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_PASS_FLAGS(name, flags, lambda) REGISTER_SYSCALL_IMPL_INTERNAL(name, SYSCALL_DEF(name), flags, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_INTERNAL(name, number, flags, lambda) \
|
||||
do { \
|
||||
FEX::HLE::x64::RegisterSyscall(Handler, FEX::HLE::x64::SYSCALL_x64_##name, (number), (flags), #name, (lambda)); \
|
||||
FEX::HLE::x32::RegisterSyscall(Handler, FEX::HLE::x32::SYSCALL_x86_##name, (number), (flags), #name, (lambda)); \
|
||||
#define REGISTER_SYSCALL_IMPL(name, lambda) \
|
||||
do { \
|
||||
FEX::HLE::x64::RegisterSyscall(Handler, FEX::HLE::x64::SYSCALL_x64_##name, #name, (lambda)); \
|
||||
FEX::HLE::x32::RegisterSyscall(Handler, FEX::HLE::x32::SYSCALL_x86_##name, #name, (lambda)); \
|
||||
} while (false)
|
||||
@@ -19,10 +19,9 @@ namespace FEX::HLE {
|
||||
void RegisterEpoll(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(epoll_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int size) -> uint64_t {
|
||||
uint64_t Result = epoll_create(size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(epoll_create, [](FEXCore::Core::CpuStateFrame* Frame, int size) -> uint64_t {
|
||||
uint64_t Result = epoll_create(size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -31,127 +31,110 @@ $end_info$
|
||||
namespace FEX::HLE {
|
||||
void RegisterFD(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(poll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, struct pollfd* fds, nfds_t nfds, int timeout) -> uint64_t {
|
||||
if (nfds) {
|
||||
// fds is allowed to be garbage if nfds is zero.
|
||||
FaultSafeUserMemAccess::VerifyIsWritable(fds, sizeof(struct pollfd) * nfds);
|
||||
}
|
||||
uint64_t Result = ::poll(fds, nfds, timeout);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(poll, [](FEXCore::Core::CpuStateFrame* Frame, struct pollfd* fds, nfds_t nfds, int timeout) -> uint64_t {
|
||||
if (nfds) {
|
||||
// fds is allowed to be garbage if nfds is zero.
|
||||
FaultSafeUserMemAccess::VerifyIsWritable(fds, sizeof(struct pollfd) * nfds);
|
||||
}
|
||||
uint64_t Result = ::poll(fds, nfds, timeout);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, int flags, uint32_t mode) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Open(pathname, flags, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(open, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, int flags, uint32_t mode) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Open(pathname, flags, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(close, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int fd) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Close(fd);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(close, [](FEXCore::Core::CpuStateFrame* Frame, int fd) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Close(fd);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(chown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, uid_t owner, gid_t group) -> uint64_t {
|
||||
uint64_t Result = ::chown(pathname, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(chown, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, uid_t owner, gid_t group) -> uint64_t {
|
||||
uint64_t Result = ::chown(pathname, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(lchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, uid_t owner, gid_t group) -> uint64_t {
|
||||
uint64_t Result = ::lchown(pathname, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(lchown, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, uid_t owner, gid_t group) -> uint64_t {
|
||||
uint64_t Result = ::lchown(pathname, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(access, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, int mode) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Access(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(access, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, int mode) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Access(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(pipe, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int pipefd[2]) -> uint64_t {
|
||||
uint64_t Result = ::pipe(pipefd);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(pipe, [](FEXCore::Core::CpuStateFrame* Frame, int pipefd[2]) -> uint64_t {
|
||||
uint64_t Result = ::pipe(pipefd);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(dup3, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int oldfd, int newfd, int flags) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = ::dup3(oldfd, newfd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(dup3, [](FEXCore::Core::CpuStateFrame* Frame, int oldfd, int newfd, int flags) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = ::dup3(oldfd, newfd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(inotify_init, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
uint64_t Result = ::inotify_init();
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(inotify_init, [](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
uint64_t Result = ::inotify_init();
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(openat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int dirfs, const char* pathname, int flags, uint32_t mode) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat(dirfs, pathname, flags, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(openat, [](FEXCore::Core::CpuStateFrame* Frame, int dirfs, const char* pathname, int flags, uint32_t mode) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat(dirfs, pathname, flags, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(readlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, char* buf, size_t bufsiz) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Readlinkat(dirfd, pathname, buf, bufsiz);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(readlinkat, [](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, char* buf, size_t bufsiz) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Readlinkat(dirfd, pathname, buf, bufsiz);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(faccessat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, int mode) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat(dirfd, pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(faccessat, [](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, int mode) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat(dirfd, pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(faccessat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, int mode, int flags) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat2(dirfd, pathname, mode, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(faccessat2, [](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, int mode, int flags) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat2(dirfd, pathname, mode, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(openat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int dirfs, const char* pathname, struct open_how* how, size_t usize) -> uint64_t {
|
||||
open_how HostHow {};
|
||||
size_t HostSize = std::min(sizeof(open_how), usize);
|
||||
memcpy(&HostHow, how, HostSize);
|
||||
REGISTER_SYSCALL_IMPL(
|
||||
openat2, [](FEXCore::Core::CpuStateFrame* Frame, int dirfs, const char* pathname, struct open_how* how, size_t usize) -> uint64_t {
|
||||
open_how HostHow {};
|
||||
size_t HostSize = std::min(sizeof(open_how), usize);
|
||||
memcpy(&HostHow, how, HostSize);
|
||||
|
||||
HostHow.flags = FEX::HLE::RemapFromX86Flags(HostHow.flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat2(dirfs, pathname, &HostHow, HostSize);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
HostHow.flags = FEX::HLE::RemapFromX86Flags(HostHow.flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat2(dirfs, pathname, &HostHow, HostSize);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(eventfd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, uint32_t count) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYSCALL_DEF(eventfd2), count, 0);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(eventfd, [](FEXCore::Core::CpuStateFrame* Frame, uint32_t count) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYSCALL_DEF(eventfd2), count, 0);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(pipe2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int pipefd[2], int flags) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = ::pipe2(pipefd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(pipe2, [](FEXCore::Core::CpuStateFrame* Frame, int pipefd[2], int flags) -> uint64_t {
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = ::pipe2(pipefd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(
|
||||
statx, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, int flags, uint32_t mask, struct statx* statxbuf) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL(
|
||||
statx, [](FEXCore::Core::CpuStateFrame* Frame, int dirfd, const char* pathname, int flags, uint32_t mask, struct statx* statxbuf) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statx(dirfd, pathname, flags, mask, statxbuf);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(close_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.CloseRange(first, last, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(close_range, [](FEXCore::Core::CpuStateFrame* Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.CloseRange(first, last, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -25,65 +25,55 @@ namespace FEX::HLE {
|
||||
void RegisterFS(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(rename, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* oldpath, const char* newpath) -> uint64_t {
|
||||
uint64_t Result = ::rename(oldpath, newpath);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(rename, [](FEXCore::Core::CpuStateFrame* Frame, const char* oldpath, const char* newpath) -> uint64_t {
|
||||
uint64_t Result = ::rename(oldpath, newpath);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(mkdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode) -> uint64_t {
|
||||
uint64_t Result = ::mkdir(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(mkdir, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode) -> uint64_t {
|
||||
uint64_t Result = ::mkdir(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(rmdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname) -> uint64_t {
|
||||
uint64_t Result = ::rmdir(pathname);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(rmdir, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname) -> uint64_t {
|
||||
uint64_t Result = ::rmdir(pathname);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(link, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* oldpath, const char* newpath) -> uint64_t {
|
||||
uint64_t Result = ::link(oldpath, newpath);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(link, [](FEXCore::Core::CpuStateFrame* Frame, const char* oldpath, const char* newpath) -> uint64_t {
|
||||
uint64_t Result = ::link(oldpath, newpath);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(unlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname) -> uint64_t {
|
||||
uint64_t Result = ::unlink(pathname);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(unlink, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname) -> uint64_t {
|
||||
uint64_t Result = ::unlink(pathname);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(symlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* target, const char* linkpath) -> uint64_t {
|
||||
uint64_t Result = ::symlink(target, linkpath);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(symlink, [](FEXCore::Core::CpuStateFrame* Frame, const char* target, const char* linkpath) -> uint64_t {
|
||||
uint64_t Result = ::symlink(target, linkpath);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(readlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, char* buf, size_t bufsiz) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Readlink(pathname, buf, bufsiz);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(readlink, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, char* buf, size_t bufsiz) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Readlink(pathname, buf, bufsiz);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(chmod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode) -> uint64_t {
|
||||
uint64_t Result = ::chmod(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(chmod, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode) -> uint64_t {
|
||||
uint64_t Result = ::chmod(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(mknod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode, dev_t dev) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Mknod(pathname, mode, dev);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(mknod, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode, dev_t dev) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Mknod(pathname, mode, dev);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(creat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode) -> uint64_t {
|
||||
uint64_t Result = ::creat(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(creat, [](FEXCore::Core::CpuStateFrame* Frame, const char* pathname, mode_t mode) -> uint64_t {
|
||||
uint64_t Result = ::creat(pathname, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(
|
||||
setxattr, [](FEXCore::Core::CpuStateFrame* Frame, const char* path, const char* name, const void* value, size_t size, int flags) -> uint64_t {
|
||||
|
||||
@@ -19,16 +19,14 @@ namespace FEX::HLE {
|
||||
void RegisterIO(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(iopl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int level) -> uint64_t {
|
||||
// Just claim we don't have permission
|
||||
return -EPERM;
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(iopl, [](FEXCore::Core::CpuStateFrame* Frame, int level) -> uint64_t {
|
||||
// Just claim we don't have permission
|
||||
return -EPERM;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(ioperm, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, unsigned long from, unsigned long num, int turn_on) -> uint64_t {
|
||||
// ioperm not available on our architecture
|
||||
return -EPERM;
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(ioperm, [](FEXCore::Core::CpuStateFrame* Frame, unsigned long from, unsigned long num, int turn_on) -> uint64_t {
|
||||
// ioperm not available on our architecture
|
||||
return -EPERM;
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -37,68 +37,65 @@ using cap_user_data_t = void*;
|
||||
void RegisterInfo(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(
|
||||
uname, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, [](FEXCore::Core::CpuStateFrame* Frame, struct utsname* buf) -> uint64_t {
|
||||
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
REGISTER_SYSCALL_IMPL(uname, [](FEXCore::Core::CpuStateFrame* Frame, struct utsname* buf) -> uint64_t {
|
||||
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
|
||||
struct utsname Local {};
|
||||
if (::uname(&Local) == 0) {
|
||||
memcpy(buf->nodename, Local.nodename, sizeof(Local.nodename));
|
||||
static_assert(sizeof(Local.nodename) <= sizeof(buf->nodename));
|
||||
memcpy(buf->domainname, Local.domainname, sizeof(Local.domainname));
|
||||
static_assert(sizeof(Local.domainname) <= sizeof(buf->domainname));
|
||||
} else {
|
||||
strcpy(buf->nodename, "FEXCore");
|
||||
LogMan::Msg::EFmt("Couldn't determine host nodename. Defaulting to '{}'", buf->nodename);
|
||||
}
|
||||
strcpy(buf->sysname, "Linux");
|
||||
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
|
||||
if (Thread->persona & UNAME26) {
|
||||
// Kernel version converts from 6.x.y to 2.6.60+x.
|
||||
GuestVersion = FEX::HLE::SyscallHandler::KernelVersion(2, 6, 60 + FEX::HLE::SyscallHandler::KernelMinor(GuestVersion));
|
||||
}
|
||||
snprintf(buf->release, sizeof(buf->release), "%d.%d.%d", FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion), FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
|
||||
struct utsname Local {};
|
||||
if (::uname(&Local) == 0) {
|
||||
memcpy(buf->nodename, Local.nodename, sizeof(Local.nodename));
|
||||
static_assert(sizeof(Local.nodename) <= sizeof(buf->nodename));
|
||||
memcpy(buf->domainname, Local.domainname, sizeof(Local.domainname));
|
||||
static_assert(sizeof(Local.domainname) <= sizeof(buf->domainname));
|
||||
} else {
|
||||
strcpy(buf->nodename, "FEXCore");
|
||||
LogMan::Msg::EFmt("Couldn't determine host nodename. Defaulting to '{}'", buf->nodename);
|
||||
}
|
||||
strcpy(buf->sysname, "Linux");
|
||||
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
|
||||
if (Thread->persona & UNAME26) {
|
||||
// Kernel version converts from 6.x.y to 2.6.60+x.
|
||||
GuestVersion = FEX::HLE::SyscallHandler::KernelVersion(2, 6, 60 + FEX::HLE::SyscallHandler::KernelMinor(GuestVersion));
|
||||
}
|
||||
snprintf(buf->release, sizeof(buf->release), "%d.%d.%d", FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion), FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
|
||||
|
||||
const char version[] = "#" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__;
|
||||
strcpy(buf->version, version);
|
||||
static_assert(sizeof(version) <= sizeof(buf->version), "uname version define became too large!");
|
||||
if (Thread->persona & PER_LINUX32) {
|
||||
// Tell the guest that we are a 32bit kernel
|
||||
strcpy(buf->machine, "i686");
|
||||
} else {
|
||||
// Tell the guest that we are a 64bit kernel
|
||||
strcpy(buf->machine, "x86_64");
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
const char version[] = "#" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__;
|
||||
strcpy(buf->version, version);
|
||||
static_assert(sizeof(version) <= sizeof(buf->version), "uname version define became too large!");
|
||||
if (Thread->persona & PER_LINUX32) {
|
||||
// Tell the guest that we are a 32bit kernel
|
||||
strcpy(buf->machine, "i686");
|
||||
} else {
|
||||
// Tell the guest that we are a 64bit kernel
|
||||
strcpy(buf->machine, "x86_64");
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(personality, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, uint32_t persona) -> uint64_t {
|
||||
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
REGISTER_SYSCALL_IMPL(personality, [](FEXCore::Core::CpuStateFrame* Frame, uint32_t persona) -> uint64_t {
|
||||
auto Thread = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
|
||||
if (persona == ~0U) {
|
||||
// Special case, only queries the persona.
|
||||
return Thread->persona;
|
||||
}
|
||||
if (persona == ~0U) {
|
||||
// Special case, only queries the persona.
|
||||
return Thread->persona;
|
||||
}
|
||||
|
||||
// Mask off `PER_LINUX32` because AArch64 doesn't support it.
|
||||
uint32_t NewPersona = persona & ~PER_LINUX32;
|
||||
// Mask off `PER_LINUX32` because AArch64 doesn't support it.
|
||||
uint32_t NewPersona = persona & ~PER_LINUX32;
|
||||
|
||||
// This syscall can not physically fail with PER_LINUX32 masked off.
|
||||
// It also can not fail on a real x86 kernel.
|
||||
(void)::syscall(SYSCALL_DEF(personality), NewPersona);
|
||||
// This syscall can not physically fail with PER_LINUX32 masked off.
|
||||
// It also can not fail on a real x86 kernel.
|
||||
(void)::syscall(SYSCALL_DEF(personality), NewPersona);
|
||||
|
||||
// Return the old persona while setting the new one.
|
||||
auto OldPersona = Thread->persona;
|
||||
Thread->persona = persona;
|
||||
return OldPersona;
|
||||
});
|
||||
// Return the old persona while setting the new one.
|
||||
auto OldPersona = Thread->persona;
|
||||
Thread->persona = persona;
|
||||
return OldPersona;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(seccomp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, unsigned int operation, unsigned int flags, void* args) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->SeccompEmulator.Handle(Frame, operation, flags, args);
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(seccomp, [](FEXCore::Core::CpuStateFrame* Frame, unsigned int operation, unsigned int flags, void* args) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->SeccompEmulator.Handle(Frame, operation, flags, args);
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(
|
||||
ptrace, [](FEXCore::Core::CpuStateFrame* Frame, int /*enum __ptrace_request*/ request, pid_t pid, void* addr, void* data) -> uint64_t {
|
||||
uint64_t Result {};
|
||||
|
||||
@@ -22,20 +22,18 @@ namespace FEX::HLE {
|
||||
void RegisterMemory(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(brk, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, void* addr) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->HandleBRK(Frame, addr);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(brk, [](FEXCore::Core::CpuStateFrame* Frame, void* addr) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->HandleBRK(Frame, addr);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(madvise, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t length, int32_t advice) -> uint64_t {
|
||||
uint64_t Result = ::madvise(addr, length, advice);
|
||||
REGISTER_SYSCALL_IMPL(madvise, [](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t length, int32_t advice) -> uint64_t {
|
||||
uint64_t Result = ::madvise(addr, length, advice);
|
||||
|
||||
if (Result != -1) {
|
||||
FEX::HLE::_SyscallHandler->TrackMadvise(Frame->Thread, (uintptr_t)addr, length, advice);
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
if (Result != -1) {
|
||||
FEX::HLE::_SyscallHandler->TrackMadvise(Frame->Thread, (uintptr_t)addr, length, advice);
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -213,349 +213,187 @@ uint64_t SyscallPassthrough7(FEXCore::Core::CpuStateFrame* Frame, uint64_t arg1,
|
||||
|
||||
void RegisterCommon(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(read, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough3<SYSCALL_DEF(read)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(write, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(write)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lseek, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(lseek)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_yield, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(sched_yield)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(msync)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mincore, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(mincore)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(shmget)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shmctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(shmctl)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getpid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(socket, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(socket)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(connect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(connect)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sendto, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(sendto)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(recvfrom, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(recvfrom)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(shutdown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(shutdown)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(bind, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough3<SYSCALL_DEF(bind)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(listen, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(listen)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getsockname, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getsockname)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpeername, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getpeername)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(socketpair, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(socketpair)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(kill, SyscallFlags::DEFAULT, SyscallPassthrough2<SYSCALL_DEF(kill)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(semget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(semget)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(msgget)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgsnd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(msgsnd)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgrcv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(msgrcv)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(msgctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(msgctl)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(flock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(flock)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(fsync)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fdatasync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(fdatasync)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(truncate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(truncate)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ftruncate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(ftruncate)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getcwd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getcwd)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(chdir)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchdir, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(fchdir)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmod, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(fchmod)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(fchown)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(umask, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(umask)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(syslog, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(syslog)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(geteuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(geteuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getegid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getegid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setpgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setpgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getppid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getppid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setsid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(setsid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setreuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setreuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setregid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setregid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getgroups)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setgroups)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(setresuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getresuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(setresgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getresgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(getpgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setfsuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setfsuid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setfsgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setfsgid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getsid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(getsid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(capget, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(capget)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(capset, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(capset)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getpriority, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getpriority)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setpriority, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(setpriority)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setparam, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(sched_setparam)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getparam, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(sched_getparam)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setscheduler, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(sched_setscheduler)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getscheduler, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(sched_getscheduler)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_get_priority_max, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(sched_get_priority_max)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_get_priority_min, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(sched_get_priority_min)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mlock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(mlock)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(munlock, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(munlock)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pivot_root, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(pivot_root)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(chroot, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(chroot)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sync, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough0<SYSCALL_DEF(sync)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(acct, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough1<SYSCALL_DEF(acct)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(mount)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(umount2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(umount2)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(swapon, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(swapon)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(swapoff, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(swapoff)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(gettid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(gettid)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsetxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(fsetxattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fgetxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(fgetxattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(flistxattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(flistxattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fremovexattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(fremovexattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tkill, SyscallFlags::DEFAULT, SyscallPassthrough2<SYSCALL_DEF(tkill)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setaffinity, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(sched_setaffinity)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getaffinity, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(sched_getaffinity)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_setup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(io_setup)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_destroy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(io_destroy)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_submit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(io_submit)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_cancel, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(io_cancel)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(remap_file_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(remap_file_pages)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timer_getoverrun, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(timer_getoverrun)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timer_delete, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(timer_delete)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tgkill, SyscallFlags::DEFAULT, SyscallPassthrough3<SYSCALL_DEF(tgkill)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mbind, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(mbind)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_mempolicy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(set_mempolicy)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(get_mempolicy, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(get_mempolicy)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mq_unlink, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(mq_unlink)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(add_key, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(add_key)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(request_key, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(request_key)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(keyctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(keyctl)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioprio_set, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(ioprio_set)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(ioprio_get, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(ioprio_get)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_add_watch, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(inotify_add_watch)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_rm_watch, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(inotify_rm_watch)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(migrate_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(migrate_pages)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mkdirat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(mkdirat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mknodat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(mknodat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchownat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(fchownat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(unlinkat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(renameat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(renameat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(linkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(linkat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(symlinkat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(symlinkat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fchmodat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(fchmodat)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(unshare, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(unshare)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(splice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(splice)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(tee, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough4<SYSCALL_DEF(tee)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(move_pages, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(move_pages)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(timerfd_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(timerfd_create)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(accept4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(accept4)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(eventfd2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(eventfd2)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(epoll_create1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(epoll_create1)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(inotify_init1, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(inotify_init1)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fanotify_init, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(fanotify_init)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fanotify_mark, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(fanotify_mark)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(prlimit_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(prlimit_64)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(name_to_handle_at, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(name_to_handle_at)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(open_by_handle_at, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(open_by_handle_at)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(syncfs, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(syncfs)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(setns, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setns)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getcpu, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getcpu)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(kcmp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY, SyscallPassthrough5<SYSCALL_DEF(kcmp)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_setattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(sched_setattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(sched_getattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(sched_getattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(renameat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(renameat2)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(getrandom, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getrandom)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(memfd_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(memfd_create)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(membarrier, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(membarrier)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mlock2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(mlock2)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(copy_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(copy_file_range)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_mprotect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(pkey_mprotect)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_alloc, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(pkey_alloc)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pkey_free, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(pkey_free)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_setup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(io_uring_setup)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_enter, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(io_uring_enter)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(io_uring_register, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(io_uring_register)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(open_tree, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(open_tree)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(move_mount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(move_mount)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsopen, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(fsopen)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsconfig, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(fsconfig)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fsmount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(fsmount)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(fspick, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(fspick)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pidfd_open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(pidfd_open)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(pidfd_getfd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(pidfd_getfd)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mount_setattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(mount_setattr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(quotactl_fd, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(quotactl_fd)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_create_ruleset, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(landlock_create_ruleset)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_add_rule, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(landlock_add_rule)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(landlock_restrict_self, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(landlock_restrict_self)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(memfd_secret, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(memfd_secret)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(process_mrelease, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(process_mrelease)>);
|
||||
REGISTER_SYSCALL_IMPL(read, SyscallPassthrough3<SYSCALL_DEF(read)>);
|
||||
REGISTER_SYSCALL_IMPL(write, SyscallPassthrough3<SYSCALL_DEF(write)>);
|
||||
REGISTER_SYSCALL_IMPL(lseek, SyscallPassthrough3<SYSCALL_DEF(lseek)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_yield, SyscallPassthrough0<SYSCALL_DEF(sched_yield)>);
|
||||
REGISTER_SYSCALL_IMPL(msync, SyscallPassthrough3<SYSCALL_DEF(msync)>);
|
||||
REGISTER_SYSCALL_IMPL(mincore, SyscallPassthrough3<SYSCALL_DEF(mincore)>);
|
||||
REGISTER_SYSCALL_IMPL(shmget, SyscallPassthrough3<SYSCALL_DEF(shmget)>);
|
||||
REGISTER_SYSCALL_IMPL(shmctl, SyscallPassthrough3<SYSCALL_DEF(shmctl)>);
|
||||
REGISTER_SYSCALL_IMPL(getpid, SyscallPassthrough0<SYSCALL_DEF(getpid)>);
|
||||
REGISTER_SYSCALL_IMPL(socket, SyscallPassthrough3<SYSCALL_DEF(socket)>);
|
||||
REGISTER_SYSCALL_IMPL(connect, SyscallPassthrough3<SYSCALL_DEF(connect)>);
|
||||
REGISTER_SYSCALL_IMPL(sendto, SyscallPassthrough6<SYSCALL_DEF(sendto)>);
|
||||
REGISTER_SYSCALL_IMPL(recvfrom, SyscallPassthrough6<SYSCALL_DEF(recvfrom)>);
|
||||
REGISTER_SYSCALL_IMPL(shutdown, SyscallPassthrough2<SYSCALL_DEF(shutdown)>);
|
||||
REGISTER_SYSCALL_IMPL(bind, SyscallPassthrough3<SYSCALL_DEF(bind)>);
|
||||
REGISTER_SYSCALL_IMPL(listen, SyscallPassthrough2<SYSCALL_DEF(listen)>);
|
||||
REGISTER_SYSCALL_IMPL(getsockname, SyscallPassthrough3<SYSCALL_DEF(getsockname)>);
|
||||
REGISTER_SYSCALL_IMPL(getpeername, SyscallPassthrough3<SYSCALL_DEF(getpeername)>);
|
||||
REGISTER_SYSCALL_IMPL(socketpair, SyscallPassthrough4<SYSCALL_DEF(socketpair)>);
|
||||
REGISTER_SYSCALL_IMPL(kill, SyscallPassthrough2<SYSCALL_DEF(kill)>);
|
||||
REGISTER_SYSCALL_IMPL(semget, SyscallPassthrough3<SYSCALL_DEF(semget)>);
|
||||
REGISTER_SYSCALL_IMPL(msgget, SyscallPassthrough2<SYSCALL_DEF(msgget)>);
|
||||
REGISTER_SYSCALL_IMPL(msgsnd, SyscallPassthrough4<SYSCALL_DEF(msgsnd)>);
|
||||
REGISTER_SYSCALL_IMPL(msgrcv, SyscallPassthrough5<SYSCALL_DEF(msgrcv)>);
|
||||
REGISTER_SYSCALL_IMPL(msgctl, SyscallPassthrough3<SYSCALL_DEF(msgctl)>);
|
||||
REGISTER_SYSCALL_IMPL(flock, SyscallPassthrough2<SYSCALL_DEF(flock)>);
|
||||
REGISTER_SYSCALL_IMPL(fsync, SyscallPassthrough1<SYSCALL_DEF(fsync)>);
|
||||
REGISTER_SYSCALL_IMPL(fdatasync, SyscallPassthrough1<SYSCALL_DEF(fdatasync)>);
|
||||
REGISTER_SYSCALL_IMPL(truncate, SyscallPassthrough2<SYSCALL_DEF(truncate)>);
|
||||
REGISTER_SYSCALL_IMPL(ftruncate, SyscallPassthrough2<SYSCALL_DEF(ftruncate)>);
|
||||
REGISTER_SYSCALL_IMPL(getcwd, SyscallPassthrough2<SYSCALL_DEF(getcwd)>);
|
||||
REGISTER_SYSCALL_IMPL(chdir, SyscallPassthrough1<SYSCALL_DEF(chdir)>);
|
||||
REGISTER_SYSCALL_IMPL(fchdir, SyscallPassthrough1<SYSCALL_DEF(fchdir)>);
|
||||
REGISTER_SYSCALL_IMPL(fchmod, SyscallPassthrough2<SYSCALL_DEF(fchmod)>);
|
||||
REGISTER_SYSCALL_IMPL(fchown, SyscallPassthrough3<SYSCALL_DEF(fchown)>);
|
||||
REGISTER_SYSCALL_IMPL(umask, SyscallPassthrough1<SYSCALL_DEF(umask)>);
|
||||
REGISTER_SYSCALL_IMPL(getuid, SyscallPassthrough0<SYSCALL_DEF(getuid)>);
|
||||
REGISTER_SYSCALL_IMPL(syslog, SyscallPassthrough3<SYSCALL_DEF(syslog)>);
|
||||
REGISTER_SYSCALL_IMPL(getgid, SyscallPassthrough0<SYSCALL_DEF(getgid)>);
|
||||
REGISTER_SYSCALL_IMPL(setuid, SyscallPassthrough1<SYSCALL_DEF(setuid)>);
|
||||
REGISTER_SYSCALL_IMPL(setgid, SyscallPassthrough1<SYSCALL_DEF(setgid)>);
|
||||
REGISTER_SYSCALL_IMPL(geteuid, SyscallPassthrough0<SYSCALL_DEF(geteuid)>);
|
||||
REGISTER_SYSCALL_IMPL(getegid, SyscallPassthrough0<SYSCALL_DEF(getegid)>);
|
||||
REGISTER_SYSCALL_IMPL(setpgid, SyscallPassthrough2<SYSCALL_DEF(setpgid)>);
|
||||
REGISTER_SYSCALL_IMPL(getppid, SyscallPassthrough0<SYSCALL_DEF(getppid)>);
|
||||
REGISTER_SYSCALL_IMPL(setsid, SyscallPassthrough0<SYSCALL_DEF(setsid)>);
|
||||
REGISTER_SYSCALL_IMPL(setreuid, SyscallPassthrough2<SYSCALL_DEF(setreuid)>);
|
||||
REGISTER_SYSCALL_IMPL(setregid, SyscallPassthrough2<SYSCALL_DEF(setregid)>);
|
||||
REGISTER_SYSCALL_IMPL(getgroups, SyscallPassthrough2<SYSCALL_DEF(getgroups)>);
|
||||
REGISTER_SYSCALL_IMPL(setgroups, SyscallPassthrough2<SYSCALL_DEF(setgroups)>);
|
||||
REGISTER_SYSCALL_IMPL(setresuid, SyscallPassthrough3<SYSCALL_DEF(setresuid)>);
|
||||
REGISTER_SYSCALL_IMPL(getresuid, SyscallPassthrough3<SYSCALL_DEF(getresuid)>);
|
||||
REGISTER_SYSCALL_IMPL(setresgid, SyscallPassthrough3<SYSCALL_DEF(setresgid)>);
|
||||
REGISTER_SYSCALL_IMPL(getresgid, SyscallPassthrough3<SYSCALL_DEF(getresgid)>);
|
||||
REGISTER_SYSCALL_IMPL(getpgid, SyscallPassthrough1<SYSCALL_DEF(getpgid)>);
|
||||
REGISTER_SYSCALL_IMPL(setfsuid, SyscallPassthrough1<SYSCALL_DEF(setfsuid)>);
|
||||
REGISTER_SYSCALL_IMPL(setfsgid, SyscallPassthrough1<SYSCALL_DEF(setfsgid)>);
|
||||
REGISTER_SYSCALL_IMPL(getsid, SyscallPassthrough1<SYSCALL_DEF(getsid)>);
|
||||
REGISTER_SYSCALL_IMPL(capget, SyscallPassthrough2<SYSCALL_DEF(capget)>);
|
||||
REGISTER_SYSCALL_IMPL(capset, SyscallPassthrough2<SYSCALL_DEF(capset)>);
|
||||
REGISTER_SYSCALL_IMPL(getpriority, SyscallPassthrough2<SYSCALL_DEF(getpriority)>);
|
||||
REGISTER_SYSCALL_IMPL(setpriority, SyscallPassthrough3<SYSCALL_DEF(setpriority)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_setparam, SyscallPassthrough2<SYSCALL_DEF(sched_setparam)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_getparam, SyscallPassthrough2<SYSCALL_DEF(sched_getparam)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_setscheduler, SyscallPassthrough3<SYSCALL_DEF(sched_setscheduler)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_getscheduler, SyscallPassthrough1<SYSCALL_DEF(sched_getscheduler)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_get_priority_max, SyscallPassthrough1<SYSCALL_DEF(sched_get_priority_max)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_get_priority_min, SyscallPassthrough1<SYSCALL_DEF(sched_get_priority_min)>);
|
||||
REGISTER_SYSCALL_IMPL(mlock, SyscallPassthrough2<SYSCALL_DEF(mlock)>);
|
||||
REGISTER_SYSCALL_IMPL(munlock, SyscallPassthrough2<SYSCALL_DEF(munlock)>);
|
||||
REGISTER_SYSCALL_IMPL(pivot_root, SyscallPassthrough2<SYSCALL_DEF(pivot_root)>);
|
||||
REGISTER_SYSCALL_IMPL(chroot, SyscallPassthrough1<SYSCALL_DEF(chroot)>);
|
||||
REGISTER_SYSCALL_IMPL(sync, SyscallPassthrough0<SYSCALL_DEF(sync)>);
|
||||
REGISTER_SYSCALL_IMPL(acct, SyscallPassthrough1<SYSCALL_DEF(acct)>);
|
||||
REGISTER_SYSCALL_IMPL(mount, SyscallPassthrough5<SYSCALL_DEF(mount)>);
|
||||
REGISTER_SYSCALL_IMPL(umount2, SyscallPassthrough2<SYSCALL_DEF(umount2)>);
|
||||
REGISTER_SYSCALL_IMPL(swapon, SyscallPassthrough2<SYSCALL_DEF(swapon)>);
|
||||
REGISTER_SYSCALL_IMPL(swapoff, SyscallPassthrough1<SYSCALL_DEF(swapoff)>);
|
||||
REGISTER_SYSCALL_IMPL(gettid, SyscallPassthrough0<SYSCALL_DEF(gettid)>);
|
||||
REGISTER_SYSCALL_IMPL(fsetxattr, SyscallPassthrough5<SYSCALL_DEF(fsetxattr)>);
|
||||
REGISTER_SYSCALL_IMPL(fgetxattr, SyscallPassthrough4<SYSCALL_DEF(fgetxattr)>);
|
||||
REGISTER_SYSCALL_IMPL(flistxattr, SyscallPassthrough3<SYSCALL_DEF(flistxattr)>);
|
||||
REGISTER_SYSCALL_IMPL(fremovexattr, SyscallPassthrough2<SYSCALL_DEF(fremovexattr)>);
|
||||
REGISTER_SYSCALL_IMPL(tkill, SyscallPassthrough2<SYSCALL_DEF(tkill)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_setaffinity, SyscallPassthrough3<SYSCALL_DEF(sched_setaffinity)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_getaffinity, SyscallPassthrough3<SYSCALL_DEF(sched_getaffinity)>);
|
||||
REGISTER_SYSCALL_IMPL(io_setup, SyscallPassthrough2<SYSCALL_DEF(io_setup)>);
|
||||
REGISTER_SYSCALL_IMPL(io_destroy, SyscallPassthrough1<SYSCALL_DEF(io_destroy)>);
|
||||
REGISTER_SYSCALL_IMPL(io_submit, SyscallPassthrough3<SYSCALL_DEF(io_submit)>);
|
||||
REGISTER_SYSCALL_IMPL(io_cancel, SyscallPassthrough3<SYSCALL_DEF(io_cancel)>);
|
||||
REGISTER_SYSCALL_IMPL(remap_file_pages, SyscallPassthrough5<SYSCALL_DEF(remap_file_pages)>);
|
||||
REGISTER_SYSCALL_IMPL(timer_getoverrun, SyscallPassthrough1<SYSCALL_DEF(timer_getoverrun)>);
|
||||
REGISTER_SYSCALL_IMPL(timer_delete, SyscallPassthrough1<SYSCALL_DEF(timer_delete)>);
|
||||
REGISTER_SYSCALL_IMPL(tgkill, SyscallPassthrough3<SYSCALL_DEF(tgkill)>);
|
||||
REGISTER_SYSCALL_IMPL(mbind, SyscallPassthrough6<SYSCALL_DEF(mbind)>);
|
||||
REGISTER_SYSCALL_IMPL(set_mempolicy, SyscallPassthrough3<SYSCALL_DEF(set_mempolicy)>);
|
||||
REGISTER_SYSCALL_IMPL(get_mempolicy, SyscallPassthrough5<SYSCALL_DEF(get_mempolicy)>);
|
||||
REGISTER_SYSCALL_IMPL(mq_unlink, SyscallPassthrough1<SYSCALL_DEF(mq_unlink)>);
|
||||
REGISTER_SYSCALL_IMPL(add_key, SyscallPassthrough5<SYSCALL_DEF(add_key)>);
|
||||
REGISTER_SYSCALL_IMPL(request_key, SyscallPassthrough4<SYSCALL_DEF(request_key)>);
|
||||
REGISTER_SYSCALL_IMPL(keyctl, SyscallPassthrough5<SYSCALL_DEF(keyctl)>);
|
||||
REGISTER_SYSCALL_IMPL(ioprio_set, SyscallPassthrough2<SYSCALL_DEF(ioprio_set)>);
|
||||
REGISTER_SYSCALL_IMPL(ioprio_get, SyscallPassthrough3<SYSCALL_DEF(ioprio_get)>);
|
||||
REGISTER_SYSCALL_IMPL(inotify_add_watch, SyscallPassthrough3<SYSCALL_DEF(inotify_add_watch)>);
|
||||
REGISTER_SYSCALL_IMPL(inotify_rm_watch, SyscallPassthrough2<SYSCALL_DEF(inotify_rm_watch)>);
|
||||
REGISTER_SYSCALL_IMPL(migrate_pages, SyscallPassthrough4<SYSCALL_DEF(migrate_pages)>);
|
||||
REGISTER_SYSCALL_IMPL(mkdirat, SyscallPassthrough3<SYSCALL_DEF(mkdirat)>);
|
||||
REGISTER_SYSCALL_IMPL(mknodat, SyscallPassthrough4<SYSCALL_DEF(mknodat)>);
|
||||
REGISTER_SYSCALL_IMPL(fchownat, SyscallPassthrough5<SYSCALL_DEF(fchownat)>);
|
||||
REGISTER_SYSCALL_IMPL(unlinkat, SyscallPassthrough3<SYSCALL_DEF(unlinkat)>);
|
||||
REGISTER_SYSCALL_IMPL(renameat, SyscallPassthrough4<SYSCALL_DEF(renameat)>);
|
||||
REGISTER_SYSCALL_IMPL(linkat, SyscallPassthrough5<SYSCALL_DEF(linkat)>);
|
||||
REGISTER_SYSCALL_IMPL(symlinkat, SyscallPassthrough3<SYSCALL_DEF(symlinkat)>);
|
||||
REGISTER_SYSCALL_IMPL(fchmodat, SyscallPassthrough3<SYSCALL_DEF(fchmodat)>);
|
||||
REGISTER_SYSCALL_IMPL(unshare, SyscallPassthrough1<SYSCALL_DEF(unshare)>);
|
||||
REGISTER_SYSCALL_IMPL(splice, SyscallPassthrough6<SYSCALL_DEF(splice)>);
|
||||
REGISTER_SYSCALL_IMPL(tee, SyscallPassthrough4<SYSCALL_DEF(tee)>);
|
||||
REGISTER_SYSCALL_IMPL(move_pages, SyscallPassthrough6<SYSCALL_DEF(move_pages)>);
|
||||
REGISTER_SYSCALL_IMPL(timerfd_create, SyscallPassthrough2<SYSCALL_DEF(timerfd_create)>);
|
||||
REGISTER_SYSCALL_IMPL(accept4, SyscallPassthrough4<SYSCALL_DEF(accept4)>);
|
||||
REGISTER_SYSCALL_IMPL(eventfd2, SyscallPassthrough2<SYSCALL_DEF(eventfd2)>);
|
||||
REGISTER_SYSCALL_IMPL(epoll_create1, SyscallPassthrough1<SYSCALL_DEF(epoll_create1)>);
|
||||
REGISTER_SYSCALL_IMPL(inotify_init1, SyscallPassthrough1<SYSCALL_DEF(inotify_init1)>);
|
||||
REGISTER_SYSCALL_IMPL(fanotify_init, SyscallPassthrough2<SYSCALL_DEF(fanotify_init)>);
|
||||
REGISTER_SYSCALL_IMPL(fanotify_mark, SyscallPassthrough5<SYSCALL_DEF(fanotify_mark)>);
|
||||
REGISTER_SYSCALL_IMPL(prlimit_64, SyscallPassthrough4<SYSCALL_DEF(prlimit_64)>);
|
||||
REGISTER_SYSCALL_IMPL(name_to_handle_at, SyscallPassthrough5<SYSCALL_DEF(name_to_handle_at)>);
|
||||
REGISTER_SYSCALL_IMPL(open_by_handle_at, SyscallPassthrough3<SYSCALL_DEF(open_by_handle_at)>);
|
||||
REGISTER_SYSCALL_IMPL(syncfs, SyscallPassthrough1<SYSCALL_DEF(syncfs)>);
|
||||
REGISTER_SYSCALL_IMPL(setns, SyscallPassthrough2<SYSCALL_DEF(setns)>);
|
||||
REGISTER_SYSCALL_IMPL(getcpu, SyscallPassthrough3<SYSCALL_DEF(getcpu)>);
|
||||
REGISTER_SYSCALL_IMPL(kcmp, SyscallPassthrough5<SYSCALL_DEF(kcmp)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_setattr, SyscallPassthrough3<SYSCALL_DEF(sched_setattr)>);
|
||||
REGISTER_SYSCALL_IMPL(sched_getattr, SyscallPassthrough4<SYSCALL_DEF(sched_getattr)>);
|
||||
REGISTER_SYSCALL_IMPL(renameat2, SyscallPassthrough5<SYSCALL_DEF(renameat2)>);
|
||||
REGISTER_SYSCALL_IMPL(getrandom, SyscallPassthrough3<SYSCALL_DEF(getrandom)>);
|
||||
REGISTER_SYSCALL_IMPL(memfd_create, SyscallPassthrough2<SYSCALL_DEF(memfd_create)>);
|
||||
REGISTER_SYSCALL_IMPL(membarrier, SyscallPassthrough2<SYSCALL_DEF(membarrier)>);
|
||||
REGISTER_SYSCALL_IMPL(mlock2, SyscallPassthrough3<SYSCALL_DEF(mlock2)>);
|
||||
REGISTER_SYSCALL_IMPL(copy_file_range, SyscallPassthrough6<SYSCALL_DEF(copy_file_range)>);
|
||||
REGISTER_SYSCALL_IMPL(pkey_mprotect, SyscallPassthrough4<SYSCALL_DEF(pkey_mprotect)>);
|
||||
REGISTER_SYSCALL_IMPL(pkey_alloc, SyscallPassthrough2<SYSCALL_DEF(pkey_alloc)>);
|
||||
REGISTER_SYSCALL_IMPL(pkey_free, SyscallPassthrough1<SYSCALL_DEF(pkey_free)>);
|
||||
REGISTER_SYSCALL_IMPL(io_uring_setup, SyscallPassthrough2<SYSCALL_DEF(io_uring_setup)>);
|
||||
REGISTER_SYSCALL_IMPL(io_uring_enter, SyscallPassthrough6<SYSCALL_DEF(io_uring_enter)>);
|
||||
REGISTER_SYSCALL_IMPL(io_uring_register, SyscallPassthrough4<SYSCALL_DEF(io_uring_register)>);
|
||||
REGISTER_SYSCALL_IMPL(open_tree, SyscallPassthrough3<SYSCALL_DEF(open_tree)>);
|
||||
REGISTER_SYSCALL_IMPL(move_mount, SyscallPassthrough5<SYSCALL_DEF(move_mount)>);
|
||||
REGISTER_SYSCALL_IMPL(fsopen, SyscallPassthrough3<SYSCALL_DEF(fsopen)>);
|
||||
REGISTER_SYSCALL_IMPL(fsconfig, SyscallPassthrough5<SYSCALL_DEF(fsconfig)>);
|
||||
REGISTER_SYSCALL_IMPL(fsmount, SyscallPassthrough3<SYSCALL_DEF(fsmount)>);
|
||||
REGISTER_SYSCALL_IMPL(fspick, SyscallPassthrough3<SYSCALL_DEF(fspick)>);
|
||||
REGISTER_SYSCALL_IMPL(pidfd_open, SyscallPassthrough2<SYSCALL_DEF(pidfd_open)>);
|
||||
REGISTER_SYSCALL_IMPL(pidfd_getfd, SyscallPassthrough3<SYSCALL_DEF(pidfd_getfd)>);
|
||||
REGISTER_SYSCALL_IMPL(mount_setattr, SyscallPassthrough5<SYSCALL_DEF(mount_setattr)>);
|
||||
REGISTER_SYSCALL_IMPL(quotactl_fd, SyscallPassthrough4<SYSCALL_DEF(quotactl_fd)>);
|
||||
REGISTER_SYSCALL_IMPL(landlock_create_ruleset, SyscallPassthrough3<SYSCALL_DEF(landlock_create_ruleset)>);
|
||||
REGISTER_SYSCALL_IMPL(landlock_add_rule, SyscallPassthrough4<SYSCALL_DEF(landlock_add_rule)>);
|
||||
REGISTER_SYSCALL_IMPL(landlock_restrict_self, SyscallPassthrough2<SYSCALL_DEF(landlock_restrict_self)>);
|
||||
REGISTER_SYSCALL_IMPL(memfd_secret, SyscallPassthrough1<SYSCALL_DEF(memfd_secret)>);
|
||||
REGISTER_SYSCALL_IMPL(process_mrelease, SyscallPassthrough2<SYSCALL_DEF(process_mrelease)>);
|
||||
if (Handler->IsHostKernelVersionAtLeast(5, 16, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(futex_waitv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(futex_waitv)>);
|
||||
REGISTER_SYSCALL_IMPL(futex_waitv, SyscallPassthrough5<SYSCALL_DEF(futex_waitv)>);
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL(futex_waitv, UnimplementedSyscallSafe);
|
||||
}
|
||||
if (Handler->IsHostKernelVersionAtLeast(5, 17, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(set_mempolicy_home_node, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(set_mempolicy_home_node)>);
|
||||
REGISTER_SYSCALL_IMPL(set_mempolicy_home_node, SyscallPassthrough4<SYSCALL_DEF(set_mempolicy_home_node)>);
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL(set_mempolicy_home_node, UnimplementedSyscallSafe);
|
||||
}
|
||||
|
||||
if (Handler->IsHostKernelVersionAtLeast(6, 8, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(futex_wake, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(futex_wake)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(futex_wait, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(futex_wait)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(futex_requeue, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(futex_requeue)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(statmount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(statmount)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(listmount, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(listmount)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lsm_get_self_attr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(lsm_get_self_attr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lsm_set_self_attr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(lsm_set_self_attr)>);
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(lsm_list_modules, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(lsm_list_modules)>);
|
||||
REGISTER_SYSCALL_IMPL(futex_wake, SyscallPassthrough4<SYSCALL_DEF(futex_wake)>);
|
||||
REGISTER_SYSCALL_IMPL(futex_wait, SyscallPassthrough6<SYSCALL_DEF(futex_wait)>);
|
||||
REGISTER_SYSCALL_IMPL(futex_requeue, SyscallPassthrough4<SYSCALL_DEF(futex_requeue)>);
|
||||
REGISTER_SYSCALL_IMPL(statmount, SyscallPassthrough4<SYSCALL_DEF(statmount)>);
|
||||
REGISTER_SYSCALL_IMPL(listmount, SyscallPassthrough4<SYSCALL_DEF(listmount)>);
|
||||
REGISTER_SYSCALL_IMPL(lsm_get_self_attr, SyscallPassthrough4<SYSCALL_DEF(lsm_get_self_attr)>);
|
||||
REGISTER_SYSCALL_IMPL(lsm_set_self_attr, SyscallPassthrough4<SYSCALL_DEF(lsm_set_self_attr)>);
|
||||
REGISTER_SYSCALL_IMPL(lsm_list_modules, SyscallPassthrough3<SYSCALL_DEF(lsm_list_modules)>);
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL(futex_wake, UnimplementedSyscallSafe);
|
||||
REGISTER_SYSCALL_IMPL(futex_wait, UnimplementedSyscallSafe);
|
||||
@@ -567,8 +405,7 @@ void RegisterCommon(FEX::HLE::SyscallHandler* Handler) {
|
||||
REGISTER_SYSCALL_IMPL(lsm_list_modules, UnimplementedSyscallSafe);
|
||||
}
|
||||
if (Handler->IsHostKernelVersionAtLeast(6, 10, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_PASS_FLAGS(mseal, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(mseal)>);
|
||||
REGISTER_SYSCALL_IMPL(mseal, SyscallPassthrough3<SYSCALL_DEF(mseal)>);
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL(mseal, UnimplementedSyscallSafe);
|
||||
}
|
||||
@@ -578,157 +415,84 @@ namespace x64 {
|
||||
void RegisterPassthrough(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
RegisterCommon(Handler);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(ioctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(ioctl)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pread_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(pread_64)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pwrite_64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(pwrite_64)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(readv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(readv)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(writev, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(writev)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(dup, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(dup)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(nanosleep, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(nanosleep)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getitimer, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getitimer)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(setitimer, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(setitimer)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sendfile, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(sendfile)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(accept, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(accept)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sendmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(sendmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(recvmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(recvmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(setsockopt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(setsockopt)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getsockopt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(getsockopt)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(wait4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(wait4)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(semop, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(semop)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(gettimeofday, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(gettimeofday)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getrlimit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getrlimit)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(getrusage, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getrusage)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sysinfo, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(sysinfo)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(times, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(times)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(rt_sigqueueinfo, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(rt_sigqueueinfo)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fstatfs, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(fstatfs)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sched_rr_get_interval, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(sched_rr_get_interval)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mlockall, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(mlockall)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(munlockall, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(munlockall)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(adjtimex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(adjtimex)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(setrlimit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setrlimit)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(settimeofday, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(settimeofday)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(readahead, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(readahead)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(futex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(futex)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(io_getevents, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(io_getevents)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(semtimedop, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(semtimedop)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timer_create, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(timer_create)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timer_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(timer_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timer_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(timer_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_getres, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_getres)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_nanosleep, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(clock_nanosleep)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_open, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(mq_open)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_timedsend, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(mq_timedsend)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_timedreceive, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(mq_timedreceive)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_notify, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(mq_notify)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(mq_getsetattr, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(mq_getsetattr)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(waitid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(waitid)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pselect6, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(pselect6)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(ppoll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(set_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(set_robust_list)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(get_robust_list, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(get_robust_list)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sync_file_range, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(sync_file_range)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(vmsplice, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(vmsplice)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(utimensat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(utimensat)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fallocate, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(fallocate)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timerfd_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(timerfd_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(timerfd_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(timerfd_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(preadv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(preadv)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pwritev, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(pwritev)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(rt_tgsigqueueinfo, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(rt_tgsigqueueinfo)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(recvmmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(recvmmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(clock_adjtime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_adjtime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(sendmmsg, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(sendmmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_vm_readv, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(process_vm_readv)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_vm_writev, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(process_vm_writev)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(preadv2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(preadv2)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pwritev2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(pwritev2)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(io_pgetevents, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(io_pgetevents)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(pidfd_send_signal, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(pidfd_send_signal)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(process_madvise, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(process_madvise)>);
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fadvise64, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(fadvise64)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(ioctl, SyscallPassthrough3<SYSCALL_DEF(ioctl)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(pread_64, SyscallPassthrough4<SYSCALL_DEF(pread_64)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(pwrite_64, SyscallPassthrough4<SYSCALL_DEF(pwrite_64)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(readv, SyscallPassthrough3<SYSCALL_DEF(readv)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(writev, SyscallPassthrough3<SYSCALL_DEF(writev)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(dup, SyscallPassthrough1<SYSCALL_DEF(dup)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(nanosleep, SyscallPassthrough2<SYSCALL_DEF(nanosleep)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(getitimer, SyscallPassthrough2<SYSCALL_DEF(getitimer)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(setitimer, SyscallPassthrough3<SYSCALL_DEF(setitimer)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(sendfile, SyscallPassthrough4<SYSCALL_DEF(sendfile)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(accept, SyscallPassthrough3<SYSCALL_DEF(accept)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(sendmsg, SyscallPassthrough3<SYSCALL_DEF(sendmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(recvmsg, SyscallPassthrough3<SYSCALL_DEF(recvmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(setsockopt, SyscallPassthrough5<SYSCALL_DEF(setsockopt)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(getsockopt, SyscallPassthrough5<SYSCALL_DEF(getsockopt)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(wait4, SyscallPassthrough4<SYSCALL_DEF(wait4)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(semop, SyscallPassthrough3<SYSCALL_DEF(semop)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(gettimeofday, SyscallPassthrough2<SYSCALL_DEF(gettimeofday)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(getrlimit, SyscallPassthrough2<SYSCALL_DEF(getrlimit)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(getrusage, SyscallPassthrough2<SYSCALL_DEF(getrusage)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(sysinfo, SyscallPassthrough1<SYSCALL_DEF(sysinfo)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(times, SyscallPassthrough1<SYSCALL_DEF(times)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(rt_sigqueueinfo, SyscallPassthrough3<SYSCALL_DEF(rt_sigqueueinfo)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(fstatfs, SyscallPassthrough2<SYSCALL_DEF(fstatfs)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(sched_rr_get_interval, SyscallPassthrough2<SYSCALL_DEF(sched_rr_get_interval)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(mlockall, SyscallPassthrough1<SYSCALL_DEF(mlockall)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(munlockall, SyscallPassthrough0<SYSCALL_DEF(munlockall)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(adjtimex, SyscallPassthrough1<SYSCALL_DEF(adjtimex)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(setrlimit, SyscallPassthrough2<SYSCALL_DEF(setrlimit)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(settimeofday, SyscallPassthrough2<SYSCALL_DEF(settimeofday)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(readahead, SyscallPassthrough3<SYSCALL_DEF(readahead)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(futex, SyscallPassthrough6<SYSCALL_DEF(futex)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(io_getevents, SyscallPassthrough5<SYSCALL_DEF(io_getevents)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(semtimedop, SyscallPassthrough4<SYSCALL_DEF(semtimedop)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(timer_create, SyscallPassthrough3<SYSCALL_DEF(timer_create)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(timer_settime, SyscallPassthrough4<SYSCALL_DEF(timer_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(timer_gettime, SyscallPassthrough2<SYSCALL_DEF(timer_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(clock_settime, SyscallPassthrough2<SYSCALL_DEF(clock_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(clock_gettime, SyscallPassthrough2<SYSCALL_DEF(clock_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(clock_getres, SyscallPassthrough2<SYSCALL_DEF(clock_getres)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(clock_nanosleep, SyscallPassthrough4<SYSCALL_DEF(clock_nanosleep)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(mq_open, SyscallPassthrough4<SYSCALL_DEF(mq_open)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(mq_timedsend, SyscallPassthrough5<SYSCALL_DEF(mq_timedsend)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(mq_timedreceive, SyscallPassthrough5<SYSCALL_DEF(mq_timedreceive)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(mq_notify, SyscallPassthrough2<SYSCALL_DEF(mq_notify)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(mq_getsetattr, SyscallPassthrough3<SYSCALL_DEF(mq_getsetattr)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(waitid, SyscallPassthrough5<SYSCALL_DEF(waitid)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(pselect6, SyscallPassthrough6<SYSCALL_DEF(pselect6)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(ppoll, SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(set_robust_list, SyscallPassthrough2<SYSCALL_DEF(set_robust_list)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(get_robust_list, SyscallPassthrough3<SYSCALL_DEF(get_robust_list)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(sync_file_range, SyscallPassthrough4<SYSCALL_DEF(sync_file_range)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(vmsplice, SyscallPassthrough4<SYSCALL_DEF(vmsplice)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(utimensat, SyscallPassthrough4<SYSCALL_DEF(utimensat)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(fallocate, SyscallPassthrough4<SYSCALL_DEF(fallocate)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(timerfd_settime, SyscallPassthrough4<SYSCALL_DEF(timerfd_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(timerfd_gettime, SyscallPassthrough2<SYSCALL_DEF(timerfd_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(preadv, SyscallPassthrough5<SYSCALL_DEF(preadv)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(pwritev, SyscallPassthrough5<SYSCALL_DEF(pwritev)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(rt_tgsigqueueinfo, SyscallPassthrough4<SYSCALL_DEF(rt_tgsigqueueinfo)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(recvmmsg, SyscallPassthrough5<SYSCALL_DEF(recvmmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(clock_adjtime, SyscallPassthrough2<SYSCALL_DEF(clock_adjtime)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(sendmmsg, SyscallPassthrough4<SYSCALL_DEF(sendmmsg)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(process_vm_readv, SyscallPassthrough6<SYSCALL_DEF(process_vm_readv)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(process_vm_writev, SyscallPassthrough6<SYSCALL_DEF(process_vm_writev)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(preadv2, SyscallPassthrough6<SYSCALL_DEF(preadv2)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(pwritev2, SyscallPassthrough6<SYSCALL_DEF(pwritev2)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(io_pgetevents, SyscallPassthrough6<SYSCALL_DEF(io_pgetevents)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(pidfd_send_signal, SyscallPassthrough4<SYSCALL_DEF(pidfd_send_signal)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(process_madvise, SyscallPassthrough5<SYSCALL_DEF(process_madvise)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(fadvise64, SyscallPassthrough4<SYSCALL_DEF(fadvise64)>);
|
||||
if (Handler->IsHostKernelVersionAtLeast(6, 5, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(cachestat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(cachestat)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(cachestat, SyscallPassthrough4<SYSCALL_DEF(cachestat)>);
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL_X64(cachestat, UnimplementedSyscallSafe);
|
||||
}
|
||||
if (Handler->IsHostKernelVersionAtLeast(6, 6, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(fchmodat2, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(fchmodat2)>);
|
||||
REGISTER_SYSCALL_IMPL_X64(fchmodat2, SyscallPassthrough4<SYSCALL_DEF(fchmodat2)>);
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL_X64(fchmodat2, UnimplementedSyscallSafe);
|
||||
}
|
||||
@@ -739,79 +503,41 @@ namespace x32 {
|
||||
void RegisterPassthrough(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
RegisterCommon(Handler);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getuid32, getuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getgid32, getgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(geteuid32, geteuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(geteuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getegid32, getegid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough0<SYSCALL_DEF(getegid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setreuid32, setreuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setreuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setregid32, setregid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setregid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getgroups32, getgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(getgroups)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setgroups32, setgroups, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(setgroups)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(fchown32, fchown, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(fchown)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setresuid32, setresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(setresuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getresuid32, getresuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getresuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setresgid32, setresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(setresgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(getresgid32, getresgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough3<SYSCALL_DEF(getresgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setuid32, setuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setgid32, setgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setfsuid32, setfsuid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setfsuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(setfsgid32, setfsgid, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough1<SYSCALL_DEF(setfsgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(sendfile64, sendfile, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(sendfile)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_gettime64, clock_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_settime64, clock_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_adjtime64, clock_adjtime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_adjtime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_getres_time64, clock_getres, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(clock_getres)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(clock_nanosleep_time64, clock_nanosleep,
|
||||
SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(clock_nanosleep)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timer_gettime64, timer_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(timer_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timer_settime64, timer_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(timer_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timerfd_gettime64, timerfd_gettime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(timerfd_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(timerfd_settime64, timerfd_settime, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(timerfd_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(utimensat_time64, utimensat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(utimensat)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(ppoll_time64, ppoll, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(io_pgetevents_time64, io_pgetevents, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(io_pgetevents)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(mq_timedsend_time64, mq_timedsend, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(mq_timedsend)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(mq_timedreceive_time64, mq_timedreceive,
|
||||
SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough5<SYSCALL_DEF(mq_timedreceive)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(semtimedop_time64, semtimedop, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough4<SYSCALL_DEF(semtimedop)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(futex_time64, futex, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough6<SYSCALL_DEF(futex)>);
|
||||
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(sched_rr_get_interval_time64, sched_rr_get_interval,
|
||||
SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
SyscallPassthrough2<SYSCALL_DEF(sched_rr_get_interval)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(getuid32, SyscallPassthrough0<SYSCALL_DEF(getuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(getgid32, SyscallPassthrough0<SYSCALL_DEF(getgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(geteuid32, SyscallPassthrough0<SYSCALL_DEF(geteuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(getegid32, SyscallPassthrough0<SYSCALL_DEF(getegid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setreuid32, SyscallPassthrough2<SYSCALL_DEF(setreuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setregid32, SyscallPassthrough2<SYSCALL_DEF(setregid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(getgroups32, SyscallPassthrough2<SYSCALL_DEF(getgroups)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setgroups32, SyscallPassthrough2<SYSCALL_DEF(setgroups)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(fchown32, SyscallPassthrough3<SYSCALL_DEF(fchown)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setresuid32, SyscallPassthrough3<SYSCALL_DEF(setresuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(getresuid32, SyscallPassthrough3<SYSCALL_DEF(getresuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setresgid32, SyscallPassthrough3<SYSCALL_DEF(setresgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(getresgid32, SyscallPassthrough3<SYSCALL_DEF(getresgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setuid32, SyscallPassthrough1<SYSCALL_DEF(setuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setgid32, SyscallPassthrough1<SYSCALL_DEF(setgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setfsuid32, SyscallPassthrough1<SYSCALL_DEF(setfsuid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(setfsgid32, SyscallPassthrough1<SYSCALL_DEF(setfsgid)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(sendfile64, SyscallPassthrough4<SYSCALL_DEF(sendfile)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(clock_gettime64, SyscallPassthrough2<SYSCALL_DEF(clock_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(clock_settime64, SyscallPassthrough2<SYSCALL_DEF(clock_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(clock_adjtime64, SyscallPassthrough2<SYSCALL_DEF(clock_adjtime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(clock_getres_time64, SyscallPassthrough2<SYSCALL_DEF(clock_getres)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(clock_nanosleep_time64, SyscallPassthrough4<SYSCALL_DEF(clock_nanosleep)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(timer_gettime64, SyscallPassthrough2<SYSCALL_DEF(timer_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(timer_settime64, SyscallPassthrough4<SYSCALL_DEF(timer_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(timerfd_gettime64, SyscallPassthrough2<SYSCALL_DEF(timerfd_gettime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(timerfd_settime64, SyscallPassthrough4<SYSCALL_DEF(timerfd_settime)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(utimensat_time64, SyscallPassthrough4<SYSCALL_DEF(utimensat)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(ppoll_time64, SyscallPassthrough5<SYSCALL_DEF(ppoll)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(io_pgetevents_time64, SyscallPassthrough6<SYSCALL_DEF(io_pgetevents)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(mq_timedsend_time64, SyscallPassthrough5<SYSCALL_DEF(mq_timedsend)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(mq_timedreceive_time64, SyscallPassthrough5<SYSCALL_DEF(mq_timedreceive)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(semtimedop_time64, SyscallPassthrough4<SYSCALL_DEF(semtimedop)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(futex_time64, SyscallPassthrough6<SYSCALL_DEF(futex)>);
|
||||
REGISTER_SYSCALL_IMPL_X32(sched_rr_get_interval_time64, SyscallPassthrough2<SYSCALL_DEF(sched_rr_get_interval)>);
|
||||
}
|
||||
} // namespace x32
|
||||
} // namespace FEX::HLE
|
||||
@@ -398,127 +398,124 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
|
||||
FEX_UNREACHABLE;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(fork, SyscallFlags::DEFAULT, ([](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
FEX::HLE::clone3_args args {.Type = TypeOfClone::TYPE_CLONE2,
|
||||
.args = {
|
||||
.flags = 0,
|
||||
.pidfd = 0,
|
||||
.child_tid = 0,
|
||||
.parent_tid = 0,
|
||||
.exit_signal = SIGCHLD,
|
||||
.stack = 0,
|
||||
.stack_size = 0,
|
||||
.tls = 0,
|
||||
.set_tid = 0,
|
||||
.set_tid_size = 0,
|
||||
.cgroup = 0,
|
||||
}};
|
||||
REGISTER_SYSCALL_IMPL(fork, ([](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
FEX::HLE::clone3_args args {.Type = TypeOfClone::TYPE_CLONE2,
|
||||
.args = {
|
||||
.flags = 0,
|
||||
.pidfd = 0,
|
||||
.child_tid = 0,
|
||||
.parent_tid = 0,
|
||||
.exit_signal = SIGCHLD,
|
||||
.stack = 0,
|
||||
.stack_size = 0,
|
||||
.tls = 0,
|
||||
.set_tid = 0,
|
||||
.set_tid_size = 0,
|
||||
.cgroup = 0,
|
||||
}};
|
||||
|
||||
return ForkGuest(Frame->Thread, Frame, &args);
|
||||
}));
|
||||
return ForkGuest(Frame->Thread, Frame, &args);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(vfork, SyscallFlags::DEFAULT, ([](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
FEX::HLE::clone3_args args {.Type = TypeOfClone::TYPE_CLONE2,
|
||||
.args = {
|
||||
.flags = CLONE_VFORK,
|
||||
.pidfd = 0,
|
||||
.child_tid = 0,
|
||||
.parent_tid = 0,
|
||||
.exit_signal = SIGCHLD,
|
||||
.stack = 0,
|
||||
.stack_size = 0,
|
||||
.tls = 0,
|
||||
.set_tid = 0,
|
||||
.set_tid_size = 0,
|
||||
.cgroup = 0,
|
||||
}};
|
||||
REGISTER_SYSCALL_IMPL(vfork, ([](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
FEX::HLE::clone3_args args {.Type = TypeOfClone::TYPE_CLONE2,
|
||||
.args = {
|
||||
.flags = CLONE_VFORK,
|
||||
.pidfd = 0,
|
||||
.child_tid = 0,
|
||||
.parent_tid = 0,
|
||||
.exit_signal = SIGCHLD,
|
||||
.stack = 0,
|
||||
.stack_size = 0,
|
||||
.tls = 0,
|
||||
.set_tid = 0,
|
||||
.set_tid_size = 0,
|
||||
.cgroup = 0,
|
||||
}};
|
||||
|
||||
return ForkGuest(Frame->Thread, Frame, &args);
|
||||
}));
|
||||
return ForkGuest(Frame->Thread, Frame, &args);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(getpgrp, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
uint64_t Result = ::getpgrp();
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(getpgrp, [](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
uint64_t Result = ::getpgrp();
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(clone3, SyscallFlags::DEFAULT,
|
||||
([](FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::kernel_clone3_args* cl_args, size_t size) -> uint64_t {
|
||||
FEX::HLE::clone3_args args {};
|
||||
args.Type = TypeOfClone::TYPE_CLONE3;
|
||||
memcpy(&args.args, cl_args, std::min(sizeof(FEX::HLE::kernel_clone3_args), size));
|
||||
return CloneHandler(Frame, &args);
|
||||
}));
|
||||
REGISTER_SYSCALL_IMPL(clone3, ([](FEXCore::Core::CpuStateFrame* Frame, FEX::HLE::kernel_clone3_args* cl_args, size_t size) -> uint64_t {
|
||||
FEX::HLE::clone3_args args {};
|
||||
args.Type = TypeOfClone::TYPE_CLONE3;
|
||||
memcpy(&args.args, cl_args, std::min(sizeof(FEX::HLE::kernel_clone3_args), size));
|
||||
return CloneHandler(Frame, &args);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(exit, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY | SyscallFlags::NORETURN,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int status) -> uint64_t {
|
||||
// TLS/DTV teardown is something FEX can't control. Disable glibc checking when we leave a pthread.
|
||||
// Since this thread is hard stopping, we can't track the TLS/DTV teardown in FEX's thread handling.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator::HardDisable();
|
||||
auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
REGISTER_SYSCALL_IMPL(exit, [](FEXCore::Core::CpuStateFrame* Frame, int status) -> uint64_t {
|
||||
// TLS/DTV teardown is something FEX can't control. Disable glibc checking when we leave a pthread.
|
||||
// Since this thread is hard stopping, we can't track the TLS/DTV teardown in FEX's thread handling.
|
||||
FEXCore::Allocator::YesIKnowImNotSupposedToUseTheGlibcAllocator::HardDisable();
|
||||
auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
|
||||
if (ThreadObject->ThreadInfo.clear_child_tid) {
|
||||
std::atomic<uint32_t>* Addr = reinterpret_cast<std::atomic<uint32_t>*>(ThreadObject->ThreadInfo.clear_child_tid);
|
||||
Addr->store(0);
|
||||
syscall(SYSCALL_DEF(futex), ThreadObject->ThreadInfo.clear_child_tid, FUTEX_WAKE, ~0ULL, 0, 0, 0);
|
||||
}
|
||||
if (ThreadObject->ThreadInfo.clear_child_tid) {
|
||||
auto Addr = std::atomic_ref<int32_t>(*ThreadObject->ThreadInfo.clear_child_tid);
|
||||
Addr.store(0);
|
||||
syscall(SYSCALL_DEF(futex), ThreadObject->ThreadInfo.clear_child_tid, FUTEX_WAKE, ~0ULL, 0, 0, 0);
|
||||
}
|
||||
|
||||
ThreadObject->StatusCode = status;
|
||||
ThreadObject->StatusCode = status;
|
||||
|
||||
FEX::HLE::_SyscallHandler->UninstallTLSState(ThreadObject);
|
||||
FEX::HLE::_SyscallHandler->UninstallTLSState(ThreadObject);
|
||||
|
||||
if (ThreadObject->ExecutionThread) {
|
||||
// If this is a pthread based execution thread, then there is more work to be done.
|
||||
// Delegate final deletion and cleanup to the pthreads Thread management.
|
||||
FEX::LinuxEmulation::Threads::LongjumpDeallocateAndExit(ThreadObject, status);
|
||||
} else {
|
||||
FEX::HLE::_SyscallHandler->TM.DestroyThread(ThreadObject, true);
|
||||
FEX::LinuxEmulation::Threads::DeallocateStackObjectAndExit(nullptr, status);
|
||||
}
|
||||
// This will never be reached
|
||||
std::terminate();
|
||||
});
|
||||
if (ThreadObject->ExecutionThread) {
|
||||
// If this is a pthread based execution thread, then there is more work to be done.
|
||||
// Delegate final deletion and cleanup to the pthreads Thread management.
|
||||
FEX::LinuxEmulation::Threads::LongjumpDeallocateAndExit(ThreadObject, status);
|
||||
} else {
|
||||
FEX::HLE::_SyscallHandler->TM.DestroyThread(ThreadObject, true);
|
||||
FEX::LinuxEmulation::Threads::DeallocateStackObjectAndExit(nullptr, status);
|
||||
}
|
||||
// This will never be reached
|
||||
std::terminate();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(prctl, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5) -> uint64_t {
|
||||
uint64_t Result {};
|
||||
REGISTER_SYSCALL_IMPL(prctl,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int option, unsigned long arg2, unsigned long arg3, unsigned long arg4,
|
||||
unsigned long arg5) -> uint64_t {
|
||||
uint64_t Result {};
|
||||
#ifndef PR_GET_AUXV
|
||||
#define PR_GET_AUXV 0x41555856
|
||||
#endif
|
||||
switch (option) {
|
||||
case PR_SET_SECCOMP: {
|
||||
uint32_t Operation {};
|
||||
if (arg2 == SECCOMP_MODE_STRICT) Operation = SECCOMP_SET_MODE_STRICT;
|
||||
if (arg2 == SECCOMP_MODE_FILTER) Operation = SECCOMP_SET_MODE_FILTER;
|
||||
switch (option) {
|
||||
case PR_SET_SECCOMP: {
|
||||
uint32_t Operation {};
|
||||
if (arg2 == SECCOMP_MODE_STRICT) Operation = SECCOMP_SET_MODE_STRICT;
|
||||
if (arg2 == SECCOMP_MODE_FILTER) Operation = SECCOMP_SET_MODE_FILTER;
|
||||
|
||||
return FEX::HLE::_SyscallHandler->SeccompEmulator.Handle(Frame, Operation, 0, reinterpret_cast<void*>(arg3));
|
||||
}
|
||||
case PR_GET_SECCOMP: return FEX::HLE::_SyscallHandler->SeccompEmulator.GetSeccomp(Frame);
|
||||
case PR_GET_AUXV: {
|
||||
if (arg4 || arg5) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return FEX::HLE::_SyscallHandler->SeccompEmulator.Handle(Frame, Operation, 0, reinterpret_cast<void*>(arg3));
|
||||
}
|
||||
case PR_GET_SECCOMP: return FEX::HLE::_SyscallHandler->SeccompEmulator.GetSeccomp(Frame);
|
||||
case PR_GET_AUXV: {
|
||||
if (arg4 || arg5) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void* addr = reinterpret_cast<void*>(arg2);
|
||||
size_t UserSize = reinterpret_cast<size_t>(arg3);
|
||||
void* addr = reinterpret_cast<void*>(arg2);
|
||||
size_t UserSize = reinterpret_cast<size_t>(arg3);
|
||||
|
||||
const auto auxv = FEX::HLE::_SyscallHandler->GetCodeLoader()->GetAuxv();
|
||||
const auto auxvBase = auxv.address;
|
||||
const auto auxvSize = auxv.size;
|
||||
size_t MinSize = std::min(auxvSize, UserSize);
|
||||
const auto auxv = FEX::HLE::_SyscallHandler->GetCodeLoader()->GetAuxv();
|
||||
const auto auxvBase = auxv.address;
|
||||
const auto auxvSize = auxv.size;
|
||||
size_t MinSize = std::min(auxvSize, UserSize);
|
||||
|
||||
memcpy(addr, reinterpret_cast<void*>(auxvBase), MinSize);
|
||||
memcpy(addr, reinterpret_cast<void*>(auxvBase), MinSize);
|
||||
|
||||
// Returns the size of auxv without truncation.
|
||||
return auxvSize;
|
||||
}
|
||||
default: Result = ::prctl(option, arg2, arg3, arg4, arg5); break;
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
// Returns the size of auxv without truncation.
|
||||
return auxvSize;
|
||||
}
|
||||
default: Result = ::prctl(option, arg2, arg3, arg4, arg5); break;
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(arch_prctl, SyscallFlags::DEFAULT, [](FEXCore::Core::CpuStateFrame* Frame, int code, unsigned long addr) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL(arch_prctl, [](FEXCore::Core::CpuStateFrame* Frame, int code, unsigned long addr) -> uint64_t {
|
||||
uint64_t Result {};
|
||||
switch (code) {
|
||||
case 0x1001: // ARCH_SET_GS
|
||||
@@ -562,22 +559,20 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(set_tid_address, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int* tidptr) -> uint64_t {
|
||||
auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
ThreadObject->ThreadInfo.clear_child_tid = tidptr;
|
||||
return ThreadObject->ThreadInfo.TID;
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(set_tid_address, [](FEXCore::Core::CpuStateFrame* Frame, int* tidptr) -> uint64_t {
|
||||
auto ThreadObject = FEX::HLE::ThreadManager::GetStateObjectFromCPUState(Frame);
|
||||
ThreadObject->ThreadInfo.clear_child_tid = tidptr;
|
||||
return ThreadObject->ThreadInfo.TID;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(exit_group, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY | SyscallFlags::NORETURN,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, int status) -> uint64_t {
|
||||
// Save telemetry if we're exiting.
|
||||
FEX::HLE::_SyscallHandler->GetSignalDelegator()->SaveTelemetry();
|
||||
FEX::HLE::_SyscallHandler->TM.CleanupForExit();
|
||||
REGISTER_SYSCALL_IMPL(exit_group, [](FEXCore::Core::CpuStateFrame* Frame, int status) -> uint64_t {
|
||||
// Save telemetry if we're exiting.
|
||||
FEX::HLE::_SyscallHandler->GetSignalDelegator()->SaveTelemetry();
|
||||
FEX::HLE::_SyscallHandler->TM.CleanupForExit();
|
||||
|
||||
syscall(SYSCALL_DEF(exit_group), status);
|
||||
// This will never be reached
|
||||
std::terminate();
|
||||
});
|
||||
syscall(SYSCALL_DEF(exit_group), status);
|
||||
// This will never be reached
|
||||
std::terminate();
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -24,16 +24,14 @@ namespace FEX::HLE {
|
||||
void RegisterTimer(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(alarm, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, unsigned int seconds) -> uint64_t {
|
||||
uint64_t Result = ::alarm(seconds);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(alarm, [](FEXCore::Core::CpuStateFrame* Frame, unsigned int seconds) -> uint64_t {
|
||||
uint64_t Result = ::alarm(seconds);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_FLAGS(pause, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
uint64_t Result = ::pause();
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL(pause, [](FEXCore::Core::CpuStateFrame* Frame) -> uint64_t {
|
||||
uint64_t Result = ::pause();
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE
|
||||
@@ -8,6 +8,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Common/FDUtils.h"
|
||||
#include "Common/FileMappingBaseAddress.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <sys/shm.h>
|
||||
@@ -23,6 +24,7 @@ $end_info$
|
||||
#include <FEXCore/Utils/SignalScopeGuards.h>
|
||||
#include <FEXCore/Utils/TypeDefines.h>
|
||||
#include <FEXHeaderUtils/Filesystem.h>
|
||||
#include <Linux/Utils/ELFParser.h>
|
||||
|
||||
namespace FEX::HLE {
|
||||
// SMC interactions
|
||||
@@ -165,22 +167,20 @@ void SyscallHandler::MarkGuestExecutableRange(FEXCore::Core::InternalThreadState
|
||||
}
|
||||
|
||||
void SyscallHandler::InvalidateGuestCodeRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Start, uint64_t Length) {
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, Start, Length);
|
||||
InvalidateCodeRangeIfNecessary(Thread, Start, Length);
|
||||
}
|
||||
|
||||
std::optional<FEXCore::ExecutableFileSectionInfo>
|
||||
SyscallHandler::LookupExecutableFileSection(FEXCore::Core::InternalThreadState& Thread, uint64_t GuestAddr) {
|
||||
auto lk = FEXCore::GuardSignalDeferringSection<std::shared_lock>(VMATracking.Mutex, &Thread);
|
||||
|
||||
// Get the first mapping after GuestAddr, or end
|
||||
// GuestAddr is inclusive
|
||||
// If the write spans two pages, they will be flushed one at a time (generating two faults)
|
||||
auto Entry = VMATracking.FindVMAEntry(GuestAddr);
|
||||
if (Entry == VMATracking.VMAs.end() || !Entry->second.Resource) {
|
||||
auto EntryIt = VMATracking.FindVMAEntry(GuestAddr);
|
||||
if (EntryIt == VMATracking.VMAs.end() || !EntryIt->second.Resource) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return FEXCore::ExecutableFileSectionInfo {*Entry->second.Resource->MappedFile, Entry->second.Base - Entry->second.Offset};
|
||||
auto& [MappingBaseAddr, Entry] = *EntryIt;
|
||||
return FEXCore::ExecutableFileSectionInfo {*Entry.Resource->MappedFile, Entry.Resource->FirstVMA->Base};
|
||||
}
|
||||
|
||||
FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) {
|
||||
@@ -195,6 +195,22 @@ FEXCore::HLE::ExecutableRangeInfo SyscallHandler::QueryGuestExecutableRange(FEXC
|
||||
return {Entry->first, Entry->second.Length, Entry->second.Prot.Writable};
|
||||
}
|
||||
|
||||
static fextl::vector<Elf64_Phdr> ReadELFHeaders(int FD, std::span<std::byte> HeaderData = {}) {
|
||||
std::string_view ELFMagic = ELFMAG;
|
||||
if (HeaderData.data()) {
|
||||
if (HeaderData.size_bytes() < ELFMagic.size() || std::memcmp(ELFMagic.data(), HeaderData.data(), ELFMagic.size()) != 0) {
|
||||
// Not an ELF file
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
// Read from FD in case the caller didn't have a mapped header available
|
||||
}
|
||||
|
||||
ELFParser Parser;
|
||||
Parser.ReadElf(dup(FD));
|
||||
return std::move(Parser.phdrs);
|
||||
}
|
||||
|
||||
void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState* Thread, void* addr, size_t length, int prot, int flags,
|
||||
int fd, off_t offset) {
|
||||
LOGMAN_THROW_A_FMT(Is64Bit || (length >> 32) == 0, "values must fit to 32 bits");
|
||||
@@ -223,10 +239,10 @@ void* SyscallHandler::GuestMmap(bool Is64Bit, FEXCore::Core::InternalThreadState
|
||||
}
|
||||
}
|
||||
|
||||
LateMetadata = FEX::HLE::_SyscallHandler->TrackMmap(Thread, Result, length, prot, flags, fd, offset);
|
||||
LateMetadata = TrackMmap(Thread, Result, length, prot, flags, fd, offset);
|
||||
}
|
||||
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, Result, Size);
|
||||
InvalidateCodeRangeIfNecessary(Thread, Result, Size);
|
||||
|
||||
if (LateMetadata) {
|
||||
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), Thread);
|
||||
@@ -250,7 +266,7 @@ uint64_t SyscallHandler::GuestMunmap(bool Is64Bit, FEXCore::Core::InternalThread
|
||||
auto lk = FEXCore::GuardSignalDeferringSectionWithFallback(VMATracking.Mutex, Thread);
|
||||
|
||||
if (reinterpret_cast<uintptr_t>(addr) < 0x1'0000'0000ULL) {
|
||||
Result = FEX::HLE::_SyscallHandler->Get32BitAllocator()->Munmap(addr, length);
|
||||
Result = Get32BitAllocator()->Munmap(addr, length);
|
||||
if (FEX::HLE::HasSyscallError(Result)) {
|
||||
return Result;
|
||||
}
|
||||
@@ -260,9 +276,9 @@ uint64_t SyscallHandler::GuestMunmap(bool Is64Bit, FEXCore::Core::InternalThread
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
FEX::HLE::_SyscallHandler->TrackMunmap(Thread, addr, length);
|
||||
TrackMunmap(Thread, addr, length);
|
||||
}
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uint64_t>(addr), Size);
|
||||
InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uint64_t>(addr), Size);
|
||||
|
||||
if (length) {
|
||||
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), Thread);
|
||||
@@ -277,23 +293,22 @@ uint64_t SyscallHandler::GuestMremap(bool Is64Bit, FEXCore::Core::InternalThread
|
||||
uint64_t Result {};
|
||||
|
||||
{
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(FEX::HLE::_SyscallHandler->VMATracking.Mutex, Thread);
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, Thread);
|
||||
if (Is64Bit) {
|
||||
Result = reinterpret_cast<uint64_t>(::mremap(old_address, old_size, new_size, flags, new_address));
|
||||
if (Result == -1) {
|
||||
return -errno;
|
||||
}
|
||||
} else {
|
||||
Result =
|
||||
reinterpret_cast<uint64_t>(FEX::HLE::_SyscallHandler->Get32BitAllocator()->Mremap(old_address, old_size, new_size, flags, new_address));
|
||||
Result = reinterpret_cast<uint64_t>(Get32BitAllocator()->Mremap(old_address, old_size, new_size, flags, new_address));
|
||||
if (FEX::HLE::HasSyscallError(Result)) {
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
FEX::HLE::_SyscallHandler->TrackMremap(Thread, reinterpret_cast<uint64_t>(old_address), old_size, new_size, flags, Result);
|
||||
TrackMremap(Thread, reinterpret_cast<uint64_t>(old_address), old_size, new_size, flags, Result);
|
||||
}
|
||||
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessaryOnRemap(Thread, reinterpret_cast<uint64_t>(old_address), Result, old_size, new_size);
|
||||
InvalidateCodeRangeIfNecessaryOnRemap(Thread, reinterpret_cast<uint64_t>(old_address), Result, old_size, new_size);
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -301,17 +316,16 @@ uint64_t SyscallHandler::GuestMprotect(FEXCore::Core::InternalThreadState* Threa
|
||||
uint64_t Result {};
|
||||
|
||||
{
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(FEX::HLE::_SyscallHandler->VMATracking.Mutex, Thread);
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, Thread);
|
||||
Result = ::mprotect(addr, len, prot);
|
||||
if (Result == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
FEX::HLE::_SyscallHandler->TrackMprotect(Thread, addr, len, prot);
|
||||
TrackMprotect(Thread, addr, len, prot);
|
||||
}
|
||||
|
||||
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uint64_t>(addr), len);
|
||||
InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uint64_t>(addr), len);
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -320,7 +334,7 @@ uint64_t SyscallHandler::GuestShmat(bool Is64Bit, FEXCore::Core::InternalThreadS
|
||||
uint64_t Length {};
|
||||
|
||||
{
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(FEX::HLE::_SyscallHandler->VMATracking.Mutex, Thread);
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, Thread);
|
||||
if (Is64Bit) {
|
||||
Result = reinterpret_cast<uint64_t>(::shmat(shmid, shmaddr, shmflg));
|
||||
if (Result == -1) {
|
||||
@@ -328,7 +342,7 @@ uint64_t SyscallHandler::GuestShmat(bool Is64Bit, FEXCore::Core::InternalThreadS
|
||||
}
|
||||
} else {
|
||||
uint32_t Addr;
|
||||
Result = FEX::HLE::_SyscallHandler->Get32BitAllocator()->Shmat(shmid, shmaddr, shmflg, &Addr);
|
||||
Result = Get32BitAllocator()->Shmat(shmid, shmaddr, shmflg, &Addr);
|
||||
if (FEX::HLE::HasSyscallError(Result)) {
|
||||
return Result;
|
||||
}
|
||||
@@ -341,10 +355,10 @@ uint64_t SyscallHandler::GuestShmat(bool Is64Bit, FEXCore::Core::InternalThreadS
|
||||
LOGMAN_THROW_A_FMT(res != -1, "shmctl IPC_STAT failed");
|
||||
|
||||
Length = stat.shm_segsz;
|
||||
FEX::HLE::_SyscallHandler->TrackShmat(Thread, shmid, Result, shmflg, Length);
|
||||
TrackShmat(Thread, shmid, Result, shmflg, Length);
|
||||
}
|
||||
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, Result, Length);
|
||||
InvalidateCodeRangeIfNecessary(Thread, Result, Length);
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -352,23 +366,23 @@ uint64_t SyscallHandler::GuestShmdt(bool Is64Bit, FEXCore::Core::InternalThreadS
|
||||
uint64_t Result {};
|
||||
uint64_t Length {};
|
||||
{
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(FEX::HLE::_SyscallHandler->VMATracking.Mutex, Thread);
|
||||
auto lk = FEXCore::GuardSignalDeferringSection(VMATracking.Mutex, Thread);
|
||||
if (Is64Bit) {
|
||||
Result = ::shmdt(shmaddr);
|
||||
if (Result == -1) {
|
||||
return -errno;
|
||||
}
|
||||
} else {
|
||||
Result = FEX::HLE::_SyscallHandler->Get32BitAllocator()->Shmdt(shmaddr);
|
||||
Result = Get32BitAllocator()->Shmdt(shmaddr);
|
||||
if (FEX::HLE::HasSyscallError(Result)) {
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
Length = FEX::HLE::_SyscallHandler->TrackShmdt(Thread, reinterpret_cast<uintptr_t>(shmaddr));
|
||||
Length = TrackShmdt(Thread, reinterpret_cast<uintptr_t>(shmaddr));
|
||||
}
|
||||
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uintptr_t>(shmaddr), Length);
|
||||
InvalidateCodeRangeIfNecessary(Thread, reinterpret_cast<uintptr_t>(shmaddr), Length);
|
||||
return Result;
|
||||
}
|
||||
|
||||
@@ -385,19 +399,56 @@ std::optional<SyscallHandler::LateApplyExtendedVolatileMetadata> SyscallHandler:
|
||||
if (!(flags & MAP_ANONYMOUS)) {
|
||||
struct stat64 buf;
|
||||
fstat64(fd, &buf);
|
||||
VMATracking::MRID mrid {buf.st_dev, buf.st_ino};
|
||||
|
||||
const VMATracking::MRID mrid {buf.st_dev, buf.st_ino};
|
||||
|
||||
char Tmp[PATH_MAX];
|
||||
auto PathLength = FEX::get_fdpath(fd, Tmp);
|
||||
|
||||
if (PathLength != -1) {
|
||||
auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0});
|
||||
Resource = &Iter->second;
|
||||
auto [ResourceIt, ResourceEnd] = VMATracking.FindResources(mrid);
|
||||
bool Inserted = false;
|
||||
const bool MappedELFHeaderAgain = ResourceIt != ResourceEnd && offset == 0 && !ResourceIt->second.ProgramHeaders.empty();
|
||||
if (ResourceIt == ResourceEnd || MappedELFHeaderAgain) {
|
||||
// Create a new MappedResource for previously unseen file and for re-mappings of an ELF header
|
||||
ResourceIt = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0});
|
||||
ResourceIt->second.Iterator = ResourceIt;
|
||||
Inserted = true;
|
||||
}
|
||||
Resource = &ResourceIt->second;
|
||||
|
||||
// Only handle FDs that are backed by regular files that are executable
|
||||
if (PathLength != -1 && S_ISREG(buf.st_mode) && (buf.st_mode & S_IXUSR)) {
|
||||
// ELF files that are mapped multiple times get a separate MappedResource for each base virtual address
|
||||
if (Inserted) {
|
||||
Resource->MappedFile = fextl::make_unique<FEXCore::ExecutableFileInfo>();
|
||||
Resource->MappedFile->Filename = fextl::string(Tmp, PathLength);
|
||||
Resource->Iterator = Iter;
|
||||
|
||||
// Read ELF headers if applicable.
|
||||
// For performance, skip ELF checks if we're not mapping the file header
|
||||
bool CheckForElfFile = (offset == 0);
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
CheckForElfFile = true;
|
||||
#endif
|
||||
if (CheckForElfFile) {
|
||||
Resource->ProgramHeaders = ReadELFHeaders(fd, std::span {reinterpret_cast<std::byte*>(addr), length});
|
||||
LOGMAN_THROW_A_FMT(Resource->ProgramHeaders.empty() || offset == 0, "Expected file offset 0 for the first mapping of an ELF "
|
||||
"file");
|
||||
}
|
||||
} else if (ResourceIt->second.ProgramHeaders.empty()) {
|
||||
// Not an ELF file, so we don't need to distinguish between different base addresses
|
||||
} else {
|
||||
// Mapped a non-header section of an ELF file.
|
||||
// Look up the corresponding MappedResource using the expected base address.
|
||||
|
||||
ResourceIt = std::find_if(ResourceIt, ResourceEnd, [&](const VMATracking::MappedResource::ContainerType::value_type& ResourcePair) {
|
||||
auto& Resource = ResourcePair.second;
|
||||
auto ExpectedBase = FEXCore::InferMappingBaseAddress(
|
||||
Resource.ProgramHeaders, addr, Size, offset,
|
||||
(ProtMapping.Executable ? PF_X : 0) | (ProtMapping.Writable ? PF_W : 0) | (ProtMapping.Readable ? PF_R : 0));
|
||||
return ExpectedBase == Resource.FirstVMA->Base;
|
||||
});
|
||||
LOGMAN_THROW_A_FMT(ResourceIt != ResourceEnd, "ERROR: Could not find base for file mapping at {:#x} (offset {:#x})", addr, offset);
|
||||
Resource = &ResourceIt->second;
|
||||
}
|
||||
|
||||
const fextl::string Filename = FHU::Filesystem::GetFilename(Resource->MappedFile->Filename);
|
||||
@@ -418,12 +469,12 @@ std::optional<SyscallHandler::LateApplyExtendedVolatileMetadata> SyscallHandler:
|
||||
} else if (flags & MAP_SHARED) {
|
||||
VMATracking::MRID mrid {VMATracking::SpecialDev::Anon, AnonSharedId++};
|
||||
|
||||
auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0});
|
||||
LOGMAN_THROW_A_FMT(Inserted == true, "VMA tracking error");
|
||||
auto [Iter, IterEnd] = VMATracking.FindResources(mrid);
|
||||
LOGMAN_THROW_A_FMT(Iter == IterEnd, "VMA tracking error");
|
||||
|
||||
Iter = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, 0});
|
||||
Resource = &Iter->second;
|
||||
Resource->Iterator = Iter;
|
||||
} else {
|
||||
Resource = nullptr;
|
||||
}
|
||||
|
||||
VMATracking.TrackVMARange(CTX, Resource, addr, offset, Size, VMATracking::VMAFlags::fromFlags(flags), ProtMapping);
|
||||
@@ -479,11 +530,12 @@ void SyscallHandler::TrackMremap(FEXCore::Core::InternalThreadState* Thread, uin
|
||||
void SyscallHandler::TrackShmat(FEXCore::Core::InternalThreadState* Thread, int shmid, uint64_t shmaddr, int shmflg, uint64_t Length) {
|
||||
VMATracking::MRID mrid {VMATracking::SpecialDev::SHM, static_cast<uint64_t>(shmid)};
|
||||
|
||||
auto [Iter, Inserted] = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, Length});
|
||||
auto Resource = &Iter->second;
|
||||
if (Inserted) {
|
||||
Resource->Iterator = Iter;
|
||||
auto [Iter, IterEnd] = VMATracking.FindResources(mrid);
|
||||
if (Iter == IterEnd) {
|
||||
Iter = VMATracking.InsertMappedResource(mrid, {nullptr, nullptr, Length});
|
||||
Iter->second.Iterator = Iter;
|
||||
}
|
||||
auto Resource = &Iter->second;
|
||||
VMATracking.TrackVMARange(CTX, Resource, shmaddr, 0, Length, VMATracking::VMAFlags::fromFlags(MAP_SHARED), VMATracking::VMAProt::fromSHM(shmflg));
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ auto VMAFlags::fromFlags(int Flags) -> VMAFlags {
|
||||
}
|
||||
|
||||
/// List Operations ///
|
||||
inline void VMATracking::ListCheckVMALinks(VMAEntry* VMA) {
|
||||
static inline void ListCheckVMALinks(const VMAEntry* VMA) {
|
||||
if (VMA) {
|
||||
LOGMAN_THROW_A_FMT(VMA->ResourceNextVMA != VMA, "VMA tracking error");
|
||||
LOGMAN_THROW_A_FMT(VMA->ResourcePrevVMA != VMA, "VMA tracking error");
|
||||
@@ -44,7 +44,7 @@ inline void VMATracking::ListCheckVMALinks(VMAEntry* VMA) {
|
||||
|
||||
// Removes a VMA from corresponding MappedResource list
|
||||
// Returns true if list is empty
|
||||
bool VMATracking::ListRemove(VMAEntry* VMA) {
|
||||
static bool ListRemove(VMAEntry* VMA) {
|
||||
LOGMAN_THROW_A_FMT(VMA->Resource != nullptr, "VMA tracking error");
|
||||
|
||||
// if it has prev, make prev to next
|
||||
@@ -78,7 +78,7 @@ bool VMATracking::ListRemove(VMAEntry* VMA) {
|
||||
|
||||
// Replaces a VMA in corresponding MappedResource list
|
||||
// Requires NewVMA->Resource, NewVMA->ResourcePrevVMA and NewVMA->ResourceNextVMA to be already setup
|
||||
void VMATracking::ListReplace(VMAEntry* VMA, VMAEntry* NewVMA) {
|
||||
static void ListReplace(VMAEntry* VMA, VMAEntry* NewVMA) {
|
||||
LOGMAN_THROW_A_FMT(VMA->Resource != nullptr, "VMA tracking error");
|
||||
|
||||
LOGMAN_THROW_A_FMT(VMA->Resource == NewVMA->Resource, "VMA tracking error");
|
||||
@@ -107,7 +107,7 @@ void VMATracking::ListReplace(VMAEntry* VMA, VMAEntry* NewVMA) {
|
||||
|
||||
// Inserts a VMA in corresponding MappedResource list
|
||||
// Requires NewVMA->Resource, NewVMA->ResourcePrevVMA and NewVMA->ResourceNextVMA to be already setup
|
||||
void VMATracking::ListInsertAfter(VMAEntry* AfterVMA, VMAEntry* NewVMA) {
|
||||
static void ListInsertAfter(VMAEntry* AfterVMA, VMAEntry* NewVMA) {
|
||||
LOGMAN_THROW_A_FMT(NewVMA->Resource != nullptr, "VMA tracking error");
|
||||
|
||||
LOGMAN_THROW_A_FMT(AfterVMA->Resource == NewVMA->Resource, "VMA tracking error");
|
||||
@@ -128,7 +128,7 @@ void VMATracking::ListInsertAfter(VMAEntry* AfterVMA, VMAEntry* NewVMA) {
|
||||
|
||||
// Prepends a VMA
|
||||
// Requires NewVMA->Resource, NewVMA->ResourcePrevVMA and NewVMA->ResourceNextVMA to be already setup
|
||||
void VMATracking::ListPrepend(MappedResource* Resource, VMAEntry* NewVMA) {
|
||||
static void ListPrepend(MappedResource* Resource, VMAEntry* NewVMA) {
|
||||
LOGMAN_THROW_A_FMT(Resource != nullptr, "VMA tracking error");
|
||||
|
||||
LOGMAN_THROW_A_FMT(NewVMA->Resource == Resource, "VMA tracking error");
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <FEXCore/fextl/memory.h>
|
||||
#include <FEXCore/Utils/SignalScopeGuards.h>
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
namespace FEX::HLE::VMATracking {
|
||||
///// VMA (Virtual Memory Area) tracking /////
|
||||
|
||||
@@ -30,15 +32,24 @@ struct MRID {
|
||||
|
||||
struct VMAEntry;
|
||||
|
||||
// Used to all MAP_SHARED VMAs of a system resource.
|
||||
/**
|
||||
* Meta data associated to one system resource.
|
||||
*
|
||||
* Typically there is one instance of this type per ELF/PE file or special device.
|
||||
* However if an ELF/PE file is mapped multiple times at different base addresses,
|
||||
* there will be one separate MappedResource for each base address. The MRID
|
||||
* is the same in this case.
|
||||
*/
|
||||
struct MappedResource {
|
||||
using ContainerType = fextl::map<MRID, MappedResource>;
|
||||
using ContainerType = fextl::multimap<MRID, MappedResource>;
|
||||
|
||||
fextl::unique_ptr<FEXCore::ExecutableFileInfo> MappedFile;
|
||||
// Pointer to lowest memory range this file is mapped to
|
||||
VMAEntry* FirstVMA;
|
||||
uint64_t Length; // 0 if not fixed size
|
||||
ContainerType::iterator Iterator;
|
||||
|
||||
fextl::vector<Elf64_Phdr> ProgramHeaders;
|
||||
};
|
||||
|
||||
union VMAProt {
|
||||
@@ -115,13 +126,16 @@ struct VMATracking {
|
||||
inline auto InsertMappedResource(const MRID& mrid, MappedResource Resource) {
|
||||
return MappedResources.emplace(mrid, std::move(Resource));
|
||||
}
|
||||
private:
|
||||
bool ListRemove(VMAEntry* Mapping);
|
||||
void ListReplace(VMAEntry* Mapping, VMAEntry* NewMapping);
|
||||
void ListInsertAfter(VMAEntry* Mapping, VMAEntry* NewMapping);
|
||||
void ListPrepend(MappedResource* Resource, VMAEntry* NewVMA);
|
||||
static void ListCheckVMALinks(VMAEntry* VMA);
|
||||
|
||||
// Returns an iterator pair spanning the range of all MappedResources matching the given MRID.
|
||||
// Typically there is only one associated resource, however sometimes the same file gets mapped
|
||||
// multiple times at different base addresses. In that case, each MappedResource will cover an
|
||||
// exclusive set of VMAEntries that refer to a consistent base mapping address.
|
||||
inline auto FindResources(const MRID& mrid) {
|
||||
return MappedResources.equal_range(mrid);
|
||||
}
|
||||
|
||||
private:
|
||||
MappedResource::ContainerType MappedResources;
|
||||
};
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ void ThreadManager::StatAlloc::Initialize() {
|
||||
goto err;
|
||||
}
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(Base), MAX_STATS_SIZE);
|
||||
|
||||
// Allocate a small working shared space for now, grow as necessary.
|
||||
{
|
||||
auto SharedBase = FEXCore::Allocator::mmap(Base, CurrentSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
|
||||
@@ -175,6 +177,9 @@ FEX::HLE::ThreadStateObject* ThreadManager::CreateThread(uint64_t InitialRIP, ui
|
||||
// Allocate the call-ret stack with guard pages on both sides
|
||||
auto AllocBase =
|
||||
reinterpret_cast<uint64_t>(FEXCore::Allocator::mmap(nullptr, CALLRET_STACK_ALLOC_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(AllocBase), CALLRET_STACK_ALLOC_SIZE);
|
||||
|
||||
// Set the base used for invalidation to the start past the guard pages
|
||||
ThreadStateObject->Thread->CallRetStackBase = reinterpret_cast<void*>(AllocBase + FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
::mprotect(ThreadStateObject->Thread->CallRetStackBase, FEXCore::Core::InternalThreadState::CALLRET_STACK_SIZE, PROT_READ | PROT_WRITE);
|
||||
@@ -199,6 +204,7 @@ FEX::HLE::ThreadStateObject* ThreadManager::CreateThread(uint64_t InitialRIP, ui
|
||||
const auto new_ldt_size = InheritThread->ldt_entry_count * FEX::HLE::SyscallHandler::LDT_ENTRY_SIZE;
|
||||
ThreadStateObject->ldt_entries = reinterpret_cast<FEXCore::Core::CPUState::gdt_segment*>(
|
||||
FEXCore::Allocator::mmap(nullptr, new_ldt_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(ThreadStateObject->ldt_entries), new_ldt_size);
|
||||
|
||||
ThreadStateObject->ldt_entry_count = InheritThread->ldt_entry_count;
|
||||
memcpy(ThreadStateObject->ldt_entries, InheritThread->ldt_entries, new_ldt_size);
|
||||
|
||||
@@ -208,10 +208,9 @@ public:
|
||||
// Thread object isn't valid very early in frontend's initialization.
|
||||
// To be more optimal the frontend should provide this code with a valid Thread object earlier.
|
||||
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), CallingThread);
|
||||
FEXCore::Context::InvalidatedEntryAccumulator Accumulator;
|
||||
|
||||
CTX->InvalidateCodeBuffersCodeRange(Start, Length);
|
||||
for (auto& Thread : Threads) {
|
||||
CTX->InvalidateGuestCodeRange(Thread->Thread, Accumulator, Start, Length);
|
||||
CTX->InvalidateThreadCachedCodeRange(Thread->Thread, Start, Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,10 +222,9 @@ public:
|
||||
// Thread object isn't valid very early in frontend's initialization.
|
||||
// To be more optimal the frontend should provide this code with a valid Thread object earlier.
|
||||
auto CodeInvalidationlk = GuardSignalDeferringSectionWithFallback(CTX->GetCodeInvalidationMutex(), CallingThread);
|
||||
FEXCore::Context::InvalidatedEntryAccumulator Accumulator;
|
||||
|
||||
CTX->InvalidateCodeBuffersCodeRange(Start, Length);
|
||||
for (auto& Thread : Threads) {
|
||||
CTX->InvalidateGuestCodeRange(Thread->Thread, Accumulator, Start, Length);
|
||||
CTX->InvalidateThreadCachedCodeRange(Thread->Thread, Start, Length);
|
||||
}
|
||||
|
||||
// Callback while holding the locks.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/LongJump.h>
|
||||
#include <FEXCore/Utils/Threads.h>
|
||||
|
||||
namespace FEX::LinuxEmulation::Threads {
|
||||
@@ -32,6 +33,7 @@ void* StackTracker::AllocateStackObject() {
|
||||
|
||||
if (Ptr == nullptr) {
|
||||
Ptr = FEXCore::Allocator::mmap(nullptr, FEX::LinuxEmulation::Threads::STACK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
FEXCore::Allocator::VirtualName("FEXMem_Misc", reinterpret_cast<void*>(Ptr), FEX::LinuxEmulation::Threads::STACK_SIZE);
|
||||
}
|
||||
|
||||
return Ptr;
|
||||
@@ -189,143 +191,6 @@ __attribute__((naked)) void StackPivotAndCall(void* Arg, FEXCore::Threads::Threa
|
||||
}
|
||||
#endif
|
||||
namespace PThreads {
|
||||
namespace LongJump {
|
||||
// This is a custom long jump implementation that avoids the glibc implementation.
|
||||
// This is required behaviour because glibc's fortification checks don't understand stack pivots.
|
||||
// FEX requires a stack pivot to work through a long jump, so these two features are at odds with each other.
|
||||
#ifdef _M_ARM_64
|
||||
struct JumpBuf {
|
||||
// All the registers that are required by AAPCS64 to save.
|
||||
// GPRs
|
||||
// X19, X20, X21, X22,
|
||||
// X23, X24, X25, X26,
|
||||
// X27, X28, X29, X30,
|
||||
//
|
||||
// Lower 64-bits:
|
||||
// V8, V9, V10, V11,
|
||||
// V12, V13, V14, V15,
|
||||
//
|
||||
// SP,
|
||||
uint64_t Registers[21];
|
||||
};
|
||||
FEX_NAKED uint64_t SetJump(JumpBuf& Buffer) {
|
||||
__asm volatile(R"(
|
||||
// x0 contains the jumpbuffer
|
||||
stp x19, x20, [x0, #( 0 * 8)];
|
||||
stp x21, x22, [x0, #( 2 * 8)];
|
||||
stp x23, x24, [x0, #( 4 * 8)];
|
||||
stp x25, x26, [x0, #( 6 * 8)];
|
||||
stp x27, x28, [x0, #( 8 * 8)];
|
||||
stp x29, x30, [x0, #(10 * 8)];
|
||||
|
||||
// FPRs
|
||||
stp d8, d9, [x0, #(12 * 8)];
|
||||
stp d10, d11, [x0, #(14 * 8)];
|
||||
stp d12, d13, [x0, #(16 * 8)];
|
||||
stp d14, d15, [x0, #(18 * 8)];
|
||||
|
||||
// Move SP in to a temporary to store.
|
||||
mov x1, sp;
|
||||
str x1, [x0, #(19 * 8)];
|
||||
|
||||
// Return zero to signify this is the SetJump.
|
||||
mov x0, #0;
|
||||
ret;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
FEX_NAKED void LongJump(JumpBuf& Buffer, uint64_t Value) {
|
||||
__asm volatile(R"(
|
||||
// x0 contains the jumpbuffer
|
||||
ldp x19, x20, [x0, #( 0 * 8)];
|
||||
ldp x21, x22, [x0, #( 2 * 8)];
|
||||
ldp x23, x24, [x0, #( 4 * 8)];
|
||||
ldp x25, x26, [x0, #( 6 * 8)];
|
||||
ldp x27, x28, [x0, #( 8 * 8)];
|
||||
ldp x29, x30, [x0, #(10 * 8)];
|
||||
|
||||
// FPRs
|
||||
ldp d8, d9, [x0, #(12 * 8)];
|
||||
ldp d10, d11, [x0, #(14 * 8)];
|
||||
ldp d12, d13, [x0, #(16 * 8)];
|
||||
ldp d14, d15, [x0, #(18 * 8)];
|
||||
|
||||
// Load SP in to temporary then move
|
||||
ldr x0, [x0, #(19 * 8)];
|
||||
mov sp, x0;
|
||||
|
||||
// Move value in to result register
|
||||
mov x0, x1;
|
||||
ret;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
#else
|
||||
struct JumpBuf {
|
||||
// Registers to preserve
|
||||
// RBX, RSP, RBP, R12, R13, R14, R15,
|
||||
// <return address>
|
||||
uint64_t Registers[8];
|
||||
};
|
||||
|
||||
__attribute__((naked)) uint64_t SetJump(JumpBuf& Buffer) {
|
||||
__asm volatile(R"(
|
||||
.intel_syntax noprefix;
|
||||
// rdi contains the jumpbuffer
|
||||
mov [rdi + (0 * 8)], rbx;
|
||||
mov [rdi + (1 * 8)], rsp;
|
||||
mov [rdi + (2 * 8)], rbp;
|
||||
mov [rdi + (3 * 8)], r12;
|
||||
mov [rdi + (4 * 8)], r13;
|
||||
mov [rdi + (5 * 8)], r14;
|
||||
mov [rdi + (6 * 8)], r15;
|
||||
|
||||
// Return address is on the stack, load it and store
|
||||
mov rsi, [rsp];
|
||||
mov [rdi + (7 * 8)], rsi;
|
||||
|
||||
// Return zero to signify this is the SetJump.
|
||||
mov rax, 0;
|
||||
ret;
|
||||
|
||||
.att_syntax prefix;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
__attribute__((naked)) void LongJump(JumpBuf& Buffer, uint64_t Value) {
|
||||
__asm volatile(R"(
|
||||
.intel_syntax noprefix;
|
||||
// rdi contains the jumpbuffer
|
||||
mov rbx, [rdi + (0 * 8)];
|
||||
mov rsp, [rdi + (1 * 8)];
|
||||
mov rbp, [rdi + (2 * 8)];
|
||||
mov r12, [rdi + (3 * 8)];
|
||||
mov r13, [rdi + (4 * 8)];
|
||||
mov r14, [rdi + (5 * 8)];
|
||||
mov r15, [rdi + (6 * 8)];
|
||||
|
||||
// Move value in to result register
|
||||
mov rax, rsi;
|
||||
|
||||
// Pop the dead return address off the stack
|
||||
pop rsi;
|
||||
|
||||
// Load the original return address from the jumpbuffer
|
||||
mov rsi, [rdi + (7 * 8)];
|
||||
|
||||
// Return using a jump
|
||||
jmp rsi;
|
||||
|
||||
.att_syntax prefix;
|
||||
)" ::
|
||||
: "memory");
|
||||
}
|
||||
#endif
|
||||
}; // namespace LongJump
|
||||
void* InitializeThread(void* Ptr);
|
||||
|
||||
class PThread final : public FEXCore::Threads::Thread {
|
||||
@@ -396,7 +261,7 @@ namespace PThreads {
|
||||
return STracker;
|
||||
}
|
||||
|
||||
void SetupLongJump(LongJump::JumpBuf* exit_resolver) {
|
||||
void SetupLongJump(FEXCore::LongJump::JumpBuf* exit_resolver) {
|
||||
_exit_resolver = exit_resolver;
|
||||
}
|
||||
|
||||
@@ -404,7 +269,7 @@ namespace PThreads {
|
||||
void LongJumpExit(FEX::HLE::ThreadStateObject* ThreadObject, uint32_t Status) {
|
||||
this->Status = Status;
|
||||
this->ThreadObject = ThreadObject;
|
||||
LongJump::LongJump(*_exit_resolver, 1);
|
||||
FEXCore::LongJump::LongJump(*_exit_resolver, 1);
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
@@ -423,7 +288,9 @@ namespace PThreads {
|
||||
void* UserArg;
|
||||
void* Stack {};
|
||||
|
||||
LongJump::JumpBuf* _exit_resolver {};
|
||||
// Use FEXCore's LongJump to avoid fortification checks.
|
||||
// This avoids a false positive since glibc does not understand stack pivots.
|
||||
FEXCore::LongJump::JumpBuf* _exit_resolver {};
|
||||
FEX::HLE::ThreadStateObject* ThreadObject {};
|
||||
uint32_t Status {};
|
||||
};
|
||||
@@ -434,11 +301,11 @@ namespace PThreads {
|
||||
PThread* Thread {reinterpret_cast<PThread*>(Ptr)};
|
||||
StackBase = Thread->GetPivotStack();
|
||||
STracker = Thread->GetStackTracker();
|
||||
LongJump::JumpBuf exit_resolver {};
|
||||
FEXCore::LongJump::JumpBuf exit_resolver {};
|
||||
|
||||
bool LongJumpExit {};
|
||||
|
||||
if (LongJump::SetJump(exit_resolver) == 0) {
|
||||
if (FEXCore::LongJump::SetJump(exit_resolver) == 0) {
|
||||
Thread->SetupLongJump(&exit_resolver);
|
||||
// Run the user function.
|
||||
// `Thread` object is dead after this function returns.
|
||||
|
||||
@@ -117,7 +117,9 @@ auto fcntlHandler = [](FEXCore::Core::CpuStateFrame* Frame, int fd, int cmd, uin
|
||||
case F_DUPFD_CLOEXEC:
|
||||
case F_GETFD:
|
||||
case F_SETFD:
|
||||
case F_GETFL: break;
|
||||
case F_GETFL:
|
||||
case F_ADD_SEALS:
|
||||
case F_GET_SEALS: break;
|
||||
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled fcntl64: 0x{:x}", cmd); break;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ void RegisterInfo(FEX::HLE::SyscallHandler* Handler) {
|
||||
uint32_t ShiftAmount {};
|
||||
if ((Host.totalram >> 32) != 0 || (Host.totalswap >> 32) != 0) {
|
||||
|
||||
while (Host.mem_unit < 4096) {
|
||||
while (Host.mem_unit < FEXCore::Utils::FEX_PAGE_SIZE) {
|
||||
Host.mem_unit <<= 1;
|
||||
++ShiftAmount;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
return FEX::HLE::SyscallHandler::GuestMunmap(false, Thread, addr, length);
|
||||
}
|
||||
|
||||
void RegisterSyscall_32(int SyscallNumber, int32_t HostSyscallNumber, FEXCore::IR::SyscallFlags Flags,
|
||||
void RegisterSyscall_32(int SyscallNumber,
|
||||
#ifdef DEBUG_STRACE
|
||||
const fextl::string& TraceFormatString,
|
||||
#endif
|
||||
@@ -63,8 +63,6 @@ public:
|
||||
#endif
|
||||
Def.Ptr = SyscallHandler;
|
||||
Def.NumArgs = ArgumentCount;
|
||||
Def.Flags = Flags;
|
||||
Def.HostSyscallNumber = HostSyscallNumber;
|
||||
#ifdef DEBUG_STRACE
|
||||
Def.StraceFmt = TraceFormatString;
|
||||
#endif
|
||||
@@ -87,12 +85,11 @@ fextl::unique_ptr<FEX::HLE::SyscallHandler> CreateHandler(FEXCore::Context::Cont
|
||||
// Deduces return, args... from the function passed
|
||||
// Does not work with lambas, because they are objects with operator (), not functions
|
||||
template<typename R, typename... Args>
|
||||
void RegisterSyscall(SyscallHandler* Handler, int SyscallNumber, int32_t HostSyscallNumber, FEXCore::IR::SyscallFlags Flags,
|
||||
const char* Name, R (*fn)(FEXCore::Core::CpuStateFrame* Frame, Args...)) {
|
||||
void RegisterSyscall(SyscallHandler* Handler, int SyscallNumber, const char* Name, R (*fn)(FEXCore::Core::CpuStateFrame* Frame, Args...)) {
|
||||
#ifdef DEBUG_STRACE
|
||||
auto TraceFormatString = fextl::string(Name) + "(" + CollectArgsFmtString<Args...>() + ") = %ld";
|
||||
#endif
|
||||
Handler->RegisterSyscall_32(SyscallNumber, HostSyscallNumber, Flags,
|
||||
Handler->RegisterSyscall_32(SyscallNumber,
|
||||
#ifdef DEBUG_STRACE
|
||||
TraceFormatString,
|
||||
#endif
|
||||
@@ -103,29 +100,14 @@ void RegisterSyscall(SyscallHandler* Handler, int SyscallNumber, int32_t HostSys
|
||||
// Non-capturing lambdas can be cast to function pointers, but this does not happen on argument matching
|
||||
// This is some glue logic that will cast a lambda and call the base RegisterSyscall implementation
|
||||
template<class F>
|
||||
void RegisterSyscall(SyscallHandler* _Handler, int num, int32_t HostSyscallNumber, FEXCore::IR::SyscallFlags Flags, const char* name, F f) {
|
||||
RegisterSyscall(_Handler, num, HostSyscallNumber, Flags, name, +f);
|
||||
void RegisterSyscall(SyscallHandler* _Handler, int num, const char* name, F f) {
|
||||
RegisterSyscall(_Handler, num, name, +f);
|
||||
}
|
||||
|
||||
} // namespace FEX::HLE::x32
|
||||
|
||||
// Registers syscall for 32bit only
|
||||
#define REGISTER_SYSCALL_IMPL_X32(name, lambda) REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, ~0, FEXCore::IR::SyscallFlags::DEFAULT, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_X32_PASS(name, lambda) \
|
||||
REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, SYSCALL_DEF(name), FEXCore::IR::SyscallFlags::DEFAULT, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(name, hostname, lambda) \
|
||||
REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, SYSCALL_DEF(hostname), FEXCore::IR::SyscallFlags::DEFAULT, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_X32_FLAGS(name, flags, lambda) REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, ~0, flags, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_X32_PASS_FLAGS(name, flags, lambda) REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, SYSCALL_DEF(name), flags, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL_FLAGS(name, hostname, flags, lambda) \
|
||||
REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, SYSCALL_DEF(hostname), flags, lambda)
|
||||
|
||||
#define REGISTER_SYSCALL_IMPL_X32_INTERNAL(name, number, flags, lambda) \
|
||||
do { \
|
||||
FEX::HLE::x32::RegisterSyscall(Handler, x32::SYSCALL_x86_##name, number, flags, #name, lambda); \
|
||||
#define REGISTER_SYSCALL_IMPL_X32(name, lambda) \
|
||||
do { \
|
||||
FEX::HLE::x32::RegisterSyscall(Handler, x32::SYSCALL_x86_##name, #name, lambda); \
|
||||
} while (false)
|
||||
@@ -19,11 +19,10 @@ void RegisterInfo(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
if (Handler->IsHostKernelVersionAtLeast(6, 6, 0)) {
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(map_shadow_stack, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, uint64_t addr, uint64_t size, uint32_t flags) -> uint64_t {
|
||||
// Claim that shadow stack isn't supported.
|
||||
return -EOPNOTSUPP;
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL_X64(map_shadow_stack, [](FEXCore::Core::CpuStateFrame* Frame, uint64_t addr, uint64_t size, uint32_t flags) -> uint64_t {
|
||||
// Claim that shadow stack isn't supported.
|
||||
return -EOPNOTSUPP;
|
||||
});
|
||||
} else {
|
||||
REGISTER_SYSCALL_IMPL_X64(map_shadow_stack, UnimplementedSyscallSafe);
|
||||
}
|
||||
|
||||
@@ -27,36 +27,30 @@ namespace FEX::HLE::x64 {
|
||||
void RegisterMemory(FEX::HLE::SyscallHandler* Handler) {
|
||||
using namespace FEXCore::IR;
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(
|
||||
mmap, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t length, int prot, int flags, int fd, off_t offset) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL_X64(
|
||||
mmap, [](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t length, int prot, int flags, int fd, off_t offset) -> uint64_t {
|
||||
return (uint64_t)FEX::HLE::_SyscallHandler->GuestMmap(Frame->Thread, addr, length, prot, flags, fd, offset);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(munmap, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t length) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestMunmap(Frame->Thread, addr, length);
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL_X64(munmap, [](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t length) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestMunmap(Frame->Thread, addr, length);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(
|
||||
mremap, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, void* old_address, size_t old_size, size_t new_size, int flags, void* new_address) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL_X64(
|
||||
mremap, [](FEXCore::Core::CpuStateFrame* Frame, void* old_address, size_t old_size, size_t new_size, int flags, void* new_address) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestMremap(true, Frame->Thread, old_address, old_size, new_size, flags, new_address);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(mprotect, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t len, int prot) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestMprotect(Frame->Thread, addr, len, prot);
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL_X64(mprotect, [](FEXCore::Core::CpuStateFrame* Frame, void* addr, size_t len, int prot) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestMprotect(Frame->Thread, addr, len, prot);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmat, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
([](FEXCore::Core::CpuStateFrame* Frame, int shmid, const void* shmaddr, int shmflg) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestShmat(true, Frame->Thread, shmid, shmaddr, shmflg);
|
||||
}));
|
||||
REGISTER_SYSCALL_IMPL_X64(shmat, ([](FEXCore::Core::CpuStateFrame* Frame, int shmid, const void* shmaddr, int shmflg) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestShmat(true, Frame->Thread, shmid, shmaddr, shmflg);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(shmdt, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
[](FEXCore::Core::CpuStateFrame* Frame, const void* shmaddr) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestShmdt(true, Frame->Thread, shmaddr);
|
||||
});
|
||||
REGISTER_SYSCALL_IMPL_X64(shmdt, [](FEXCore::Core::CpuStateFrame* Frame, const void* shmaddr) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GuestShmdt(true, Frame->Thread, shmaddr);
|
||||
});
|
||||
}
|
||||
} // namespace FEX::HLE::x64
|
||||
@@ -64,9 +64,7 @@ void x64SyscallHandler::RegisterSyscallHandlers() {
|
||||
|
||||
const SyscallFunctionDefinition InvalidSyscall {
|
||||
.Ptr = reinterpret_cast<void*>(&UnimplementedSyscall),
|
||||
.HostSyscallNumber = SYSCALL_DEF(MAX),
|
||||
.NumArgs = 0,
|
||||
.Flags = FEXCore::IR::SyscallFlags::DEFAULT,
|
||||
#ifdef DEBUG_STRACE
|
||||
.StraceFmt = "Invalid",
|
||||
#endif
|
||||
|
||||
Loaded 100 of 580 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user