diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 15:13:31 -0500 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 15:13:31 -0500 |
| commit | f21d6c0a49a90fd6ec919de438a07da11ead12d9 (patch) | |
| tree | daa3d3123b3e6274ed0f1dd4b4400ff9be10f503 /src/dynarmic | |
| parent | 1337d7f4650045d7aa2a50db93a0fbdcadefd55d (diff) | |
| parent | 34f81e052c37973a69aeec39e2f996b89c80e4ad (diff) | |
Merge remote-tracking branch 'origin/fix/dynarmic-compact-block-tables' into test/linux-all-prs-plus
Diffstat (limited to 'src/dynarmic')
9 files changed, 151 insertions, 97 deletions
diff --git a/src/dynarmic/src/dynarmic/backend/block_range_information.cpp b/src/dynarmic/src/dynarmic/backend/block_range_information.cpp index 1a56b7adb6..068e86c2bc 100644 --- a/src/dynarmic/src/dynarmic/backend/block_range_information.cpp +++ b/src/dynarmic/src/dynarmic/backend/block_range_information.cpp @@ -8,7 +8,8 @@ #include "dynarmic/backend/block_range_information.h" -#include <boost/icl/interval_map.hpp> +#include <algorithm> + #include <boost/icl/interval_set.hpp> #include "common/common_types.h" #include "common/container/unordered_set.h" @@ -17,24 +18,28 @@ namespace Dynarmic::Backend { template<typename P> void BlockRangeInformation<P>::AddRange(boost::icl::discrete_interval<P> range, IR::LocationDescriptor location) { - block_ranges.add(std::make_pair(range, ::Common::unordered_set<IR::LocationDescriptor>{location})); + if (!boost::icl::is_empty(range)) { + ranges.push_back({boost::icl::first(range), boost::icl::last(range), location}); + } } template<typename P> void BlockRangeInformation<P>::ClearCache() { - block_ranges.clear(); + ranges.clear(); } template<typename P> -::Common::unordered_set<IR::LocationDescriptor> BlockRangeInformation<P>::InvalidateRanges(const boost::icl::interval_set<P>& ranges) { +::Common::unordered_set<IR::LocationDescriptor> BlockRangeInformation<P>::InvalidateRanges(const boost::icl::interval_set<P>& invalidated) { ::Common::unordered_set<IR::LocationDescriptor> erase_locations; - for (auto invalidate_interval : ranges) { - auto pair = block_ranges.equal_range(invalidate_interval); - for (auto it = pair.first; it != pair.second; ++it) - for (const auto& descriptor : it->second) - erase_locations.insert(descriptor); - } - // TODO: EFFICIENCY: Remove ranges that are to be erased. + std::erase_if(ranges, [&](const Range& range) { + for (const auto& interval : invalidated) { + if (range.first <= boost::icl::last(interval) && boost::icl::first(interval) <= range.last) { + erase_locations.insert(range.location); + return true; + } + } + return false; + }); return erase_locations; } diff --git a/src/dynarmic/src/dynarmic/backend/block_range_information.h b/src/dynarmic/src/dynarmic/backend/block_range_information.h index 8f7daa9d9c..f894240603 100644 --- a/src/dynarmic/src/dynarmic/backend/block_range_information.h +++ b/src/dynarmic/src/dynarmic/backend/block_range_information.h @@ -8,11 +8,9 @@ #pragma once -#include <set> +#include <vector> -#include <boost/icl/interval_map.hpp> #include <boost/icl/interval_set.hpp> -#include "common/container/unordered_map.h" #include "common/container/unordered_set.h" #include "dynarmic/ir/location_descriptor.h" @@ -24,8 +22,18 @@ class BlockRangeInformation { public: void AddRange(boost::icl::discrete_interval<P> range, IR::LocationDescriptor location); void ClearCache(); - ::Common::unordered_set<IR::LocationDescriptor> InvalidateRanges(const boost::icl::interval_set<P>& ranges); - boost::icl::interval_map<P, ::Common::unordered_set<IR::LocationDescriptor>> block_ranges; + ::Common::unordered_set<IR::LocationDescriptor> InvalidateRanges(const boost::icl::interval_set<P>& invalidated); + +private: + /// The guest code [first, last] a block was translated from. Code is invalidated rarely (when + /// it is unmapped or reprotected), so a list scanned then costs least: an interval map of + /// block sets held ~300 bytes per block. + struct Range { + P first; + P last; + IR::LocationDescriptor location; + }; + std::vector<Range> ranges; }; } // namespace Dynarmic::Backend diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index 5c6a90e15a..e0e3c463d1 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -1168,7 +1168,7 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD } else { if (e.conf.enable_cycle_counting) { e.code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); - e.patch_information[terminal.next].jg.push_back(e.code.getCurr()); + e.AddPatchLocation(terminal.next, EmitX64::PatchKind::Jg); if (const auto next_bb = e.GetBasicBlock(terminal.next)) { e.EmitPatchJg(terminal.next, next_bb->entrypoint); } else { @@ -1176,7 +1176,7 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD } } else { e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0); - e.patch_information[terminal.next].jz.push_back(e.code.getCurr()); + e.AddPatchLocation(terminal.next, EmitX64::PatchKind::Jz); if (const auto next_bb = e.GetBasicBlock(terminal.next)) { e.EmitPatchJz(terminal.next, next_bb->entrypoint); } else { @@ -1196,7 +1196,7 @@ bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); e.code.ReturnFromRunCode(); } else { - e.patch_information[terminal.next].jmp.push_back(e.code.getCurr()); + e.AddPatchLocation(terminal.next, EmitX64::PatchKind::Jmp); if (const auto next_bb = e.GetBasicBlock(terminal.next)) { e.EmitPatchJmp(terminal.next, next_bb->entrypoint); } else { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h index a106bb6482..eb36bffd88 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h @@ -87,13 +87,17 @@ public: // Fastmem information using DoNotFastmemMarker = std::tuple<IR::LocationDescriptor, unsigned>; + /// Where a fastmem access that faults goes: code addresses are offsets in the code space. struct FastmemPatchInfo { - u64 resume_rip; - u64 callback; - DoNotFastmemMarker marker; - bool recompile; + u32 rip; + u32 resume_rip; + u32 callback; + u32 inst_name : 31; + u32 recompile : 1; + IR::LocationDescriptor location; }; std::optional<DoNotFastmemMarker> ShouldFastmem(A32EmitContext& ctx, IR::Inst* inst) const; + void AddFastmemPatch(CodePtr location, CodePtr resume_location, void (*callback)(), const DoNotFastmemMarker& marker, bool recompile); FakeCall FastmemCallback(u64 rip); // Memory access helpers @@ -127,7 +131,7 @@ public: RegAlloc reg_alloc; //reusable reg alloc BlockRangeInformation<u32> block_ranges; std::array<FastDispatchEntry, fast_dispatch_table_size> fast_dispatch_table; - ::Common::unordered_map<u64, FastmemPatchInfo> fastmem_patch_info; + std::vector<FastmemPatchInfo> fastmem_patch_info; // sorted by rip ::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks; ::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks; ::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks; diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index 71e5e62cd9..2307226744 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -628,7 +628,7 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { if (e.conf.enable_cycle_counting) { e.code.cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0); - e.patch_information[terminal.next].jg.push_back(e.code.getCurr()); + e.AddPatchLocation(terminal.next, EmitX64::PatchKind::Jg); if (const auto next_bb = e.GetBasicBlock(terminal.next)) { e.EmitPatchJg(terminal.next, next_bb->entrypoint); } else { @@ -636,7 +636,7 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD } } else { e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0); - e.patch_information[terminal.next].jz.push_back(e.code.getCurr()); + e.AddPatchLocation(terminal.next, EmitX64::PatchKind::Jz); if (const auto next_bb = e.GetBasicBlock(terminal.next)) { e.EmitPatchJz(terminal.next, next_bb->entrypoint); } else { @@ -656,7 +656,7 @@ bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { - e.patch_information[terminal.next].jmp.push_back(e.code.getCurr()); + e.AddPatchLocation(terminal.next, EmitX64::PatchKind::Jmp); if (auto next_bb = e.GetBasicBlock(terminal.next)) { e.EmitPatchJmp(terminal.next, next_bb->entrypoint); } else { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h index 1310b4ad34..a9f2d9b650 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h @@ -83,13 +83,17 @@ public: // Fastmem information using DoNotFastmemMarker = std::tuple<IR::LocationDescriptor, unsigned>; + /// Where a fastmem access that faults goes: code addresses are offsets in the code space. struct FastmemPatchInfo { - u64 resume_rip; - u64 callback; - DoNotFastmemMarker marker; - bool recompile; + u32 rip; + u32 resume_rip; + u32 callback; + u32 inst_name : 31; + u32 recompile : 1; + IR::LocationDescriptor location; }; std::optional<DoNotFastmemMarker> ShouldFastmem(A64EmitContext& ctx, IR::Inst* inst) const; + void AddFastmemPatch(CodePtr location, CodePtr resume_location, void (*callback)(), const DoNotFastmemMarker& marker, bool recompile); FakeCall FastmemCallback(u64 rip); // Memory access helpers @@ -123,7 +127,7 @@ public: RegAlloc reg_alloc; //reusable reg alloc BlockRangeInformation<u64> block_ranges; std::array<FastDispatchEntry, fast_dispatch_table_size> fast_dispatch_table; - ::Common::unordered_map<u64, FastmemPatchInfo> fastmem_patch_info; + std::vector<FastmemPatchInfo> fastmem_patch_info; // sorted by rip ::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> read_fallbacks; ::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> write_fallbacks; ::Common::unordered_map<std::tuple<bool, size_t, int, int>, void (*)()> exclusive_write_fallbacks; diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp index b9df3987ca..84b9888bf5 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.cpp @@ -102,7 +102,7 @@ void EmitX64::PushRSBHelper(Xbyak::Reg64 loc_desc_reg, Xbyak::Reg64 index_reg, I code.mov(index_reg.cvt32(), dword[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_rsb_ptr]); code.mov(loc_desc_reg, target.Value()); - patch_information[target].mov_rcx.push_back(code.getCurr()); + AddPatchLocation(target, PatchKind::MovRcx); EmitPatchMovRcx(target_code_ptr); code.mov(qword[code.ABI_JIT_PTR + index_reg * 8 + code.GetJitStateInfo().offsetof_rsb_location_descriptors], loc_desc_reg); code.mov(qword[code.ABI_JIT_PTR + index_reg * 8 + code.GetJitStateInfo().offsetof_rsb_codeptrs], rcx); @@ -357,37 +357,61 @@ EmitX64::BlockDescriptor EmitX64::RegisterBlock(const IR::LocationDescriptor& de return block_desc; } -void EmitX64::Patch(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { - const CodePtr save_code_ptr = code.getCurr(); - const PatchInformation& patch_info = patch_information[target_desc]; - - for (CodePtr location : patch_info.jg) { - code.SetCodePtr(location); - EmitPatchJg(target_desc, target_code_ptr); +void EmitX64::AddPatchLocation(const IR::LocationDescriptor& target_desc, PatchKind kind) { + const size_t offset = code.getCurr<const u8*>() - static_cast<const u8*>(code.getCode()); + ASSERT(offset > 0 && offset < (size_t(1) << 30)); + const u32 entry = u32(offset << 2) | u32(kind); + PatchInformation& patch_info = patch_information[target_desc]; + for (u32& slot : patch_info.first) { + if (slot == 0) { + slot = entry; + return; + } } - - for (CodePtr location : patch_info.jz) { - code.SetCodePtr(location); - EmitPatchJz(target_desc, target_code_ptr); + if (!patch_info.rest) { + patch_info.rest = std::make_unique<std::vector<u32>>(); } + patch_info.rest->push_back(entry); +} - for (CodePtr location : patch_info.jmp) { - code.SetCodePtr(location); - EmitPatchJmp(target_desc, target_code_ptr); +void EmitX64::Patch(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { + const auto it = patch_information.find(target_desc); + if (it == patch_information.end()) { + return; } - - for (CodePtr location : patch_info.mov_rcx) { - code.SetCodePtr(location); - EmitPatchMovRcx(target_code_ptr); + const CodePtr save_code_ptr = code.getCurr(); + const auto patch = [&](u32 entry) { + code.SetCodePtr(static_cast<const u8*>(code.getCode()) + (entry >> 2)); + switch (PatchKind(entry & 3)) { + case PatchKind::Jg: + EmitPatchJg(target_desc, target_code_ptr); + break; + case PatchKind::Jz: + EmitPatchJz(target_desc, target_code_ptr); + break; + case PatchKind::Jmp: + EmitPatchJmp(target_desc, target_code_ptr); + break; + case PatchKind::MovRcx: + EmitPatchMovRcx(target_code_ptr); + break; + } + }; + for (const u32 entry : it->second.first) { + if (entry != 0) { + patch(entry); + } + } + if (it->second.rest) { + for (const u32 entry : *it->second.rest) { + patch(entry); + } } - code.SetCodePtr(save_code_ptr); } void EmitX64::Unpatch(const IR::LocationDescriptor& target_desc) { - if (patch_information.count(target_desc)) { - Patch(target_desc, nullptr); - } + Patch(target_desc, nullptr); } void EmitX64::ClearCache() { diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h index 5e540e5f70..327cac7668 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h @@ -116,12 +116,16 @@ public: virtual bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0; // Patching + enum class PatchKind : u32 { Jg, Jz, Jmp, MovRcx }; + /// Places in emitted code that jump to a block (or load its entrypoint), patched when it is + /// compiled or invalidated: each the place's offset in the code space times four plus its + /// PatchKind, 0 when unused. Most blocks are linked from one or two places. struct PatchInformation { - boost::container::small_vector<CodePtr, 4> jg; //4*8=32 - boost::container::small_vector<CodePtr, 4> jz; //4*8=32 - boost::container::small_vector<CodePtr, 4> jmp; //4*8=32 - boost::container::small_vector<CodePtr, 4> mov_rcx; //4*8=32 + std::array<u32, 2> first{}; + std::unique_ptr<std::vector<u32>> rest; }; + /// Records that the code about to be emitted at the current position is a patch of `kind`. + void AddPatchLocation(const IR::LocationDescriptor& target_desc, PatchKind kind); void Patch(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr); virtual void Unpatch(const IR::LocationDescriptor& target_desc); virtual void EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr = nullptr) = 0; diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc index 8bb8be3d5b..689e507abe 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64_memory.cpp.inc @@ -30,15 +30,48 @@ std::optional<AxxEmitX64::DoNotFastmemMarker> AxxEmitX64::ShouldFastmem(AxxEmitC return marker; } +void AxxEmitX64::AddFastmemPatch(CodePtr location, CodePtr resume_location, void (*callback)(), const DoNotFastmemMarker& marker, bool recompile) { + const u64 base = std::bit_cast<u64>(code.getCode()); + const auto offset = [base](u64 address) { + ASSERT(address >= base && address - base <= UINT32_MAX); + return u32(address - base); + }; + FastmemPatchInfo info{ + .rip = offset(std::bit_cast<u64>(location)), + .resume_rip = offset(std::bit_cast<u64>(resume_location)), + .callback = offset(std::bit_cast<u64>(callback)), + .inst_name = 0, + .recompile = 0, + .location = std::get<0>(marker), + }; + info.inst_name = std::get<1>(marker); + info.recompile = recompile ? 1 : 0; + // Code is emitted in address order; only a block's deferred fallbacks can come after a later + // access of the same block. + auto it = fastmem_patch_info.end(); + if (!fastmem_patch_info.empty() && fastmem_patch_info.back().rip >= info.rip) { + it = std::lower_bound(fastmem_patch_info.begin(), fastmem_patch_info.end(), info.rip, + [](const FastmemPatchInfo& entry, u32 value) { return entry.rip < value; }); + if (it != fastmem_patch_info.end() && it->rip == info.rip) { + return; + } + } + fastmem_patch_info.insert(it, info); +} + FakeCall AxxEmitX64::FastmemCallback(u64 rip_) { - const auto iter = fastmem_patch_info.find(rip_); - if (iter != fastmem_patch_info.end()) { + const u64 base = std::bit_cast<u64>(code.getCode()); + const auto iter = rip_ >= base && rip_ - base <= UINT32_MAX + ? std::lower_bound(fastmem_patch_info.begin(), fastmem_patch_info.end(), u32(rip_ - base), + [](const FastmemPatchInfo& entry, u32 value) { return entry.rip < value; }) + : fastmem_patch_info.end(); + if (iter != fastmem_patch_info.end() && iter->rip == u32(rip_ - base)) { FakeCall result{ - .call_rip = iter->second.callback, - .ret_rip = iter->second.resume_rip, + .call_rip = base + iter->callback, + .ret_rip = base + iter->resume_rip, }; - if (iter->second.recompile) { - const auto marker = iter->second.marker; + if (iter->recompile) { + const DoNotFastmemMarker marker{iter->location, static_cast<unsigned>(iter->inst_name)}; do_not_fastmem.insert(marker); InvalidateBasicBlocks({std::get<0>(marker)}); } @@ -99,14 +132,7 @@ void AxxEmitX64::EmitMemoryRead(AxxEmitContext& ctx, IR::Inst* inst) { code.L(*abort); code.call(wrapped_fn); - fastmem_patch_info.emplace( - std::bit_cast<u64>(location), - FastmemPatchInfo{ - std::bit_cast<u64>(code.getCurr()), - std::bit_cast<u64>(wrapped_fn), - *fastmem_marker, - conf.recompile_on_fastmem_failure, - }); + AddFastmemPatch(location, code.getCurr(), wrapped_fn, *fastmem_marker, conf.recompile_on_fastmem_failure); EmitCheckMemoryAbort(ctx, inst, end); code.jmp(*end, code.T_NEAR); @@ -186,14 +212,7 @@ void AxxEmitX64::EmitMemoryWrite(AxxEmitContext& ctx, IR::Inst* inst) { code.L(*abort); code.call(wrapped_fn); - fastmem_patch_info.emplace( - std::bit_cast<u64>(location), - FastmemPatchInfo{ - std::bit_cast<u64>(code.getCurr()), - std::bit_cast<u64>(wrapped_fn), - *fastmem_marker, - conf.recompile_on_fastmem_failure, - }); + AddFastmemPatch(location, code.getCurr(), wrapped_fn, *fastmem_marker, conf.recompile_on_fastmem_failure); EmitCheckMemoryAbort(ctx, inst, end); code.jmp(*end, code.T_NEAR); @@ -357,14 +376,7 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in const auto location = EmitReadMemoryMov<bitsize>(code, value_idx, src_ptr, ordered); - fastmem_patch_info.emplace( - std::bit_cast<u64>(location), - FastmemPatchInfo{ - std::bit_cast<u64>(code.getCurr()), - std::bit_cast<u64>(wrapped_fn), - *fastmem_marker, - conf.recompile_on_exclusive_fastmem_failure, - }); + AddFastmemPatch(location, code.getCurr(), wrapped_fn, *fastmem_marker, conf.recompile_on_exclusive_fastmem_failure); code.L(*end); @@ -495,14 +507,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i code.L(*abort); code.call(wrapped_fn); - fastmem_patch_info.emplace( - std::bit_cast<u64>(location), - FastmemPatchInfo{ - std::bit_cast<u64>(code.getCurr()), - std::bit_cast<u64>(wrapped_fn), - *fastmem_marker, - conf.recompile_on_exclusive_fastmem_failure, - }); + AddFastmemPatch(location, code.getCurr(), wrapped_fn, *fastmem_marker, conf.recompile_on_exclusive_fastmem_failure); code.xor_(status.cvt32(), status.cvt32()); //dep-break code.test(code.al, code.al); |
