[LINT] Linted files + Added lint job to CI
This commit is contained in:
parent
e8afad8f8a
commit
b9061e6292
|
@ -44,37 +44,51 @@ on:
|
|||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: windows-2022
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check Clang-Format Version
|
||||
run: clang-format --version
|
||||
|
||||
- name: Lint
|
||||
run: .\xb lint --all
|
||||
|
||||
build-windows:
|
||||
name: Build (Windows) # runner.os can't be used here
|
||||
runs-on: windows-2022
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
needs: lint
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup
|
||||
run: .\xb setup
|
||||
|
||||
- name: Build
|
||||
run: .\xb build --config=Release --target=src\xenia-app --target=src\xenia-vfs-dump
|
||||
run: .\xb build --config=Release --target=src\xenia-app
|
||||
|
||||
- name: Prepare artifacts
|
||||
run: |
|
||||
robocopy . build\bin\${{ runner.os }}\Release LICENSE /r:0 /w:0
|
||||
robocopy build\bin\${{ runner.os }}\Release artifacts\xenia_canary xenia_canary.exe xenia_canary.pdb LICENSE /r:0 /w:0
|
||||
robocopy build\bin\${{ runner.os }}\Release artifacts\xenia-vfs-dump xenia-vfs-dump.exe xenia-vfs-dump.pdb LICENSE /r:0 /w:0
|
||||
If ($LastExitCode -le 7) { echo "LastExitCode = $LastExitCode";$LastExitCode = 0 }
|
||||
- name: Upload xenia-vfs-dump artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xenia-vfs-dump_canary
|
||||
path: artifacts\xenia-vfs-dump
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload xenia canary artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xenia_canary
|
||||
path: artifacts\xenia_canary
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Create release
|
||||
if: |
|
||||
github.repository == 'xenia-canary/xenia-canary' &&
|
||||
|
|
|
@ -35,9 +35,10 @@
|
|||
// and let the normal AudioSystem handling take it, to prevent duplicate
|
||||
// implementations. They can be found in xboxkrnl_audio_xma.cc
|
||||
|
||||
DEFINE_uint32(
|
||||
apu_max_queued_frames, 64,
|
||||
"Allows changing max buffered audio frames to reduce audio delay. Minimum is 16.", "APU");
|
||||
DEFINE_uint32(apu_max_queued_frames, 64,
|
||||
"Allows changing max buffered audio frames to reduce audio "
|
||||
"delay. Minimum is 16.",
|
||||
"APU");
|
||||
|
||||
namespace xe {
|
||||
namespace apu {
|
||||
|
@ -76,11 +77,14 @@ X_STATUS AudioSystem::Setup(kernel::KernelState* kernel_state) {
|
|||
}
|
||||
|
||||
worker_running_ = true;
|
||||
worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
||||
new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() {
|
||||
worker_thread_ =
|
||||
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||
kernel_state, 128 * 1024, 0,
|
||||
[this]() {
|
||||
WorkerThreadMain();
|
||||
return 0;
|
||||
}, kernel_state->GetSystemProcess()));
|
||||
},
|
||||
kernel_state->GetSystemProcess()));
|
||||
// As we run audio callbacks the debugger must be able to suspend us.
|
||||
worker_thread_->set_can_debugger_suspend(true);
|
||||
worker_thread_->set_name("Audio Worker");
|
||||
|
|
|
@ -21,7 +21,6 @@ namespace conversion {
|
|||
|
||||
#if XE_ARCH_AMD64
|
||||
|
||||
|
||||
XE_NOINLINE
|
||||
static void _generic_sequential_6_BE_to_interleaved_6_LE(
|
||||
float* XE_RESTRICT output, const float* XE_RESTRICT input,
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/cpu/processor.h"
|
||||
#include "xenia/cpu/thread_state.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
extern "C" {
|
||||
#include "third_party/FFmpeg/libavutil/log.h"
|
||||
} // extern "C"
|
||||
|
@ -102,8 +102,7 @@ void av_log_callback(void* avcl, int level, const char* fmt, va_list va) {
|
|||
StringBuffer buff;
|
||||
buff.AppendVarargs(fmt, va);
|
||||
xe::logging::AppendLogLineFormat(LogSrc::Apu, log_level, level_char,
|
||||
"ffmpeg: {}",
|
||||
buff.to_string_view());
|
||||
"ffmpeg: {}", buff.to_string_view());
|
||||
}
|
||||
|
||||
X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
||||
|
@ -141,11 +140,16 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) {
|
|||
worker_running_ = true;
|
||||
work_event_ = xe::threading::Event::CreateAutoResetEvent(false);
|
||||
assert_not_null(work_event_);
|
||||
worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
||||
new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() {
|
||||
worker_thread_ =
|
||||
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||
kernel_state, 128 * 1024, 0,
|
||||
[this]() {
|
||||
WorkerThreadMain();
|
||||
return 0;
|
||||
}, kernel_state->GetIdleProcess()));//this one doesnt need any process actually. never calls any guest code
|
||||
},
|
||||
kernel_state
|
||||
->GetIdleProcess())); // this one doesnt need any process
|
||||
// actually. never calls any guest code
|
||||
worker_thread_->set_name("XMA Decoder");
|
||||
worker_thread_->set_can_debugger_suspend(true);
|
||||
worker_thread_->Create();
|
||||
|
|
|
@ -39,8 +39,8 @@ class Clock {
|
|||
// Host tick count. Generally QueryHostTickCount() should be used.
|
||||
static uint64_t host_tick_count_platform();
|
||||
#if XE_CLOCK_RAW_AVAILABLE
|
||||
//chrispy: the way msvc was ordering the branches was causing rdtsc to be speculatively executed each time
|
||||
//the branch history was lost
|
||||
// chrispy: the way msvc was ordering the branches was causing rdtsc to be
|
||||
// speculatively executed each time the branch history was lost
|
||||
XE_NOINLINE
|
||||
static uint64_t host_tick_count_raw();
|
||||
#endif
|
||||
|
|
|
@ -41,9 +41,6 @@
|
|||
"\n" \
|
||||
"Set the cvar 'clock_source_raw' to 'false'.");
|
||||
|
||||
|
||||
|
||||
|
||||
namespace xe {
|
||||
// Getting the TSC frequency can be a bit tricky. This method here only works on
|
||||
// Intel as it seems. There is no easy way to get the frequency outside of ring0
|
||||
|
@ -75,8 +72,6 @@ uint64_t Clock::host_tick_frequency_raw() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (max_cpuid >= 0x15) {
|
||||
// 15H Get TSC/Crystal ratio and Crystal Hz.
|
||||
xe_cpu_cpuid(0x15, eax, ebx, ecx, edx);
|
||||
|
@ -98,7 +93,6 @@ uint64_t Clock::host_tick_frequency_raw() {
|
|||
return cpu_base_freq;
|
||||
}
|
||||
|
||||
|
||||
CLOCK_FATAL("The clock frequency could not be determined.");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ static bool has_shell_environment_variable() {
|
|||
}
|
||||
|
||||
void AttachConsole() {
|
||||
|
||||
bool has_console = ::AttachConsole(ATTACH_PARENT_PROCESS) == TRUE;
|
||||
#if 0
|
||||
if (!has_console || !has_shell_environment_variable()) {
|
||||
|
|
|
@ -172,8 +172,7 @@ CommandVar<T>::CommandVar(const char* name, T* default_value,
|
|||
default_value_(*default_value),
|
||||
current_value_(default_value),
|
||||
commandline_value_(),
|
||||
description_(description)
|
||||
{}
|
||||
description_(description) {}
|
||||
|
||||
template <class T>
|
||||
ConfigVar<T>::ConfigVar(const char* name, T* default_value,
|
||||
|
|
|
@ -606,7 +606,8 @@ union IDivExtraInfo {
|
|||
} info;
|
||||
};
|
||||
// returns magicnum multiplier
|
||||
static constexpr uint32_t PregenerateUint32Div(uint32_t _denom, uint32_t& out_extra) {
|
||||
static constexpr uint32_t PregenerateUint32Div(uint32_t _denom,
|
||||
uint32_t& out_extra) {
|
||||
IDivExtraInfo extra{};
|
||||
|
||||
uint32_t d = _denom;
|
||||
|
@ -662,7 +663,8 @@ static constexpr uint32_t ApplyUint32Div(uint32_t num, uint32_t mul,
|
|||
|
||||
extra.value_ = extradata;
|
||||
|
||||
uint32_t result = static_cast<uint32_t>((static_cast<uint64_t>(num) * static_cast<uint64_t>(mul)) >> 32);
|
||||
uint32_t result = static_cast<uint32_t>(
|
||||
(static_cast<uint64_t>(num) * static_cast<uint64_t>(mul)) >> 32);
|
||||
if (extra.info.add_) {
|
||||
uint32_t addend = result + num;
|
||||
addend = ((addend < result ? 0x80000000 : 0) | addend);
|
||||
|
@ -672,7 +674,8 @@ static constexpr uint32_t ApplyUint32Div(uint32_t num, uint32_t mul,
|
|||
}
|
||||
|
||||
static constexpr uint32_t ApplyUint32UMod(uint32_t num, uint32_t mul,
|
||||
uint32_t extradata, uint32_t original) {
|
||||
uint32_t extradata,
|
||||
uint32_t original) {
|
||||
uint32_t dived = ApplyUint32Div(num, mul, extradata);
|
||||
unsigned result = num - (dived * original);
|
||||
|
||||
|
@ -701,8 +704,7 @@ struct MagicDiv {
|
|||
return extra.info.shift_;
|
||||
}
|
||||
|
||||
constexpr uint32_t GetMultiplier() const { return multiplier_;
|
||||
}
|
||||
constexpr uint32_t GetMultiplier() const { return multiplier_; }
|
||||
constexpr uint32_t Apply(uint32_t numerator) const {
|
||||
return ApplyUint32Div(numerator, multiplier_, extradata_);
|
||||
}
|
||||
|
|
|
@ -180,7 +180,8 @@ static void vastcpy_impl_repmovs(CacheLine* XE_RESTRICT physaddr,
|
|||
__movsq((unsigned long long*)physaddr, (unsigned long long*)rdmapping,
|
||||
written_length / 8);
|
||||
#else
|
||||
memcpy((unsigned char*)physaddr, (const unsigned char*)rdmapping, written_length);
|
||||
memcpy((unsigned char*)physaddr, (const unsigned char*)rdmapping,
|
||||
written_length);
|
||||
#endif
|
||||
}
|
||||
XE_COLD
|
||||
|
@ -331,9 +332,9 @@ void copy_and_swap_32_unaligned(void* dest_ptr, const void* src_ptr,
|
|||
|
||||
__m256i output1 = _mm256_shuffle_epi8(input1, shufmask);
|
||||
__m256i output2 = _mm256_shuffle_epi8(input2, shufmask);
|
||||
//chrispy: todo, benchmark this w/ and w/out these prefetches here on multiple machines
|
||||
//finding a good distance for prefetchw in particular is probably important
|
||||
//for when we're writing across 2 cachelines
|
||||
// chrispy: todo, benchmark this w/ and w/out these prefetches here on multiple
|
||||
// machines finding a good distance for prefetchw in particular is probably
|
||||
// important for when we're writing across 2 cachelines
|
||||
#if 0
|
||||
if (i + 48 <= count) {
|
||||
swcache::PrefetchNTA(&src[i + 32]);
|
||||
|
|
|
@ -17,10 +17,8 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
#include "xenia/base/byte_order.h"
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace memory {
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#ifndef XENIA_BASE_MUTEX_H_
|
||||
#define XENIA_BASE_MUTEX_H_
|
||||
#include <mutex>
|
||||
#include "platform.h"
|
||||
#include "memory.h"
|
||||
#include "platform.h"
|
||||
#define XE_ENABLE_FAST_WIN32_MUTEX 1
|
||||
namespace xe {
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@ class RingBuffer {
|
|||
ring_size_t offset_delta = write_offs - read_offs;
|
||||
ring_size_t wrap_read_count = (cap - read_offs) + write_offs;
|
||||
|
||||
|
||||
if (XE_LIKELY(read_offs <= write_offs)) {
|
||||
return offset_delta; // will be 0 if they are equal, semantically
|
||||
// identical to old code (i checked the asm, msvc
|
||||
|
|
|
@ -34,7 +34,6 @@ struct SimpleFreelist {
|
|||
node->next_ = head_;
|
||||
head_ = node;
|
||||
}
|
||||
void Reset() { head_ = nullptr;
|
||||
}
|
||||
void Reset() { head_ = nullptr; }
|
||||
};
|
||||
} // namespace xe
|
|
@ -33,7 +33,9 @@ using WaitItem = TimerQueueWaitItem;
|
|||
*/
|
||||
|
||||
/*
|
||||
edit: actually had to change it back, when i was testing it only worked because i fixed disruptorplus' code to compile (it gives wrong args to condition_variable::wait_until) but now builds
|
||||
edit: actually had to change it back, when i was testing it only worked
|
||||
because i fixed disruptorplus' code to compile (it gives wrong args to
|
||||
condition_variable::wait_until) but now builds
|
||||
|
||||
*/
|
||||
using WaitStrat = dp::blocking_wait_strategy;
|
||||
|
|
|
@ -78,7 +78,8 @@ class Backend {
|
|||
virtual void InitializeBackendContext(void* ctx) {}
|
||||
|
||||
/*
|
||||
Free any dynamically allocated data/resources that the backendcontext uses
|
||||
Free any dynamically allocated data/resources that the backendcontext
|
||||
uses
|
||||
*/
|
||||
virtual void DeinitializeBackendContext(void* ctx) {}
|
||||
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode){};
|
||||
|
|
|
@ -314,7 +314,6 @@ SIMPLE_THREEOPERAND(vpshaw, xop_VPSHAW)
|
|||
SIMPLE_THREEOPERAND(vpshad, xop_VPSHAD)
|
||||
SIMPLE_THREEOPERAND(vpshaq, xop_VPSHAQ)
|
||||
|
||||
|
||||
SIMPLE_THREEOPERAND(vpshlb, xop_VPSHLB)
|
||||
SIMPLE_THREEOPERAND(vpshlw, xop_VPSHLW)
|
||||
SIMPLE_THREEOPERAND(vpshld, xop_VPSHLD)
|
||||
|
|
|
@ -924,7 +924,8 @@ void* X64HelperEmitter::EmitScalarVRsqrteHelper() {
|
|||
Xbyak::Label L18, L2, L35, L4, L9, L8, L10, L11, L12, L13, L1;
|
||||
Xbyak::Label LC1, _LCPI3_1;
|
||||
Xbyak::Label handle_denormal_input;
|
||||
Xbyak::Label specialcheck_1, convert_to_signed_inf_and_ret, handle_oddball_denormal;
|
||||
Xbyak::Label specialcheck_1, convert_to_signed_inf_and_ret,
|
||||
handle_oddball_denormal;
|
||||
|
||||
auto emulate_lzcnt_helper_unary_reg = [this](auto& reg, auto& scratch_reg) {
|
||||
inLocalLabel();
|
||||
|
@ -1107,7 +1108,8 @@ void* X64HelperEmitter::EmitScalarVRsqrteHelper() {
|
|||
xchg(ecx, edx);
|
||||
// esi is just the value of xmm0's low word, so we can restore it from there
|
||||
shl(r8d, cl);
|
||||
mov(ecx, edx); // restore ecx, dont xchg because we're going to spoil edx anyway
|
||||
mov(ecx,
|
||||
edx); // restore ecx, dont xchg because we're going to spoil edx anyway
|
||||
mov(edx, r8d);
|
||||
vmovd(r8d, xmm0);
|
||||
}
|
||||
|
@ -1131,7 +1133,8 @@ void* X64HelperEmitter::EmitScalarVRsqrteHelper() {
|
|||
dd(0xFF800000);
|
||||
dd(0x7F800000);
|
||||
L(LC1);
|
||||
//the position of 7FC00000 here matters, this address will be indexed in handle_oddball_denormal
|
||||
// the position of 7FC00000 here matters, this address will be indexed in
|
||||
// handle_oddball_denormal
|
||||
dd(0x7FC00000);
|
||||
dd(0x5F34FD00);
|
||||
|
||||
|
@ -1148,11 +1151,13 @@ void* X64HelperEmitter::EmitVectorVRsqrteHelper(void* scalar_helper) {
|
|||
Xbyak::Label check_scalar_operation_in_vmx, actual_vector_version;
|
||||
auto result_ptr =
|
||||
GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_xmms[0]));
|
||||
auto counter_ptr = GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_u64s[2]));
|
||||
auto counter_ptr =
|
||||
GetBackendCtxPtr(offsetof(X64BackendContext, helper_scratch_u64s[2]));
|
||||
counter_ptr.setBit(64);
|
||||
|
||||
// shuffle and xor to check whether all lanes are equal
|
||||
//sadly has to leave the float pipeline for the vptest, which is moderate yikes
|
||||
// sadly has to leave the float pipeline for the vptest, which is moderate
|
||||
// yikes
|
||||
vmovhlps(xmm2, xmm0, xmm0);
|
||||
vmovsldup(xmm1, xmm0);
|
||||
vxorps(xmm1, xmm1, xmm0);
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/bit_map.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/cpu/backend/backend.h"
|
||||
|
||||
#if XE_PLATFORM_WIN32 == 1
|
||||
|
@ -45,8 +45,9 @@ typedef void (*ResolveFunctionThunk)();
|
|||
|
||||
/*
|
||||
place guest trampolines in the memory range that the HV normally occupies.
|
||||
This way guests can call in via the indirection table and we don't have to clobber/reuse an existing memory range
|
||||
The xboxkrnl range is already used by export trampolines (see kernel/kernel_module.cc)
|
||||
This way guests can call in via the indirection table and we don't have to
|
||||
clobber/reuse an existing memory range The xboxkrnl range is already used by
|
||||
export trampolines (see kernel/kernel_module.cc)
|
||||
*/
|
||||
static constexpr uint32_t GUEST_TRAMPOLINE_BASE = 0x80000000;
|
||||
static constexpr uint32_t GUEST_TRAMPOLINE_END = 0x80040000;
|
||||
|
@ -78,8 +79,10 @@ struct X64BackendStackpoint {
|
|||
enum : uint32_t {
|
||||
kX64BackendMXCSRModeBit = 0,
|
||||
kX64BackendHasReserveBit = 1,
|
||||
kX64BackendNJMOn = 2, //non-java mode bit is currently set. for use in software fp routines
|
||||
kX64BackendNonIEEEMode = 3, //non-ieee mode is currently enabled for scalar fpu.
|
||||
kX64BackendNJMOn =
|
||||
2, // non-java mode bit is currently set. for use in software fp routines
|
||||
kX64BackendNonIEEEMode =
|
||||
3, // non-ieee mode is currently enabled for scalar fpu.
|
||||
};
|
||||
// located prior to the ctx register
|
||||
// some things it would be nice to have be per-emulator instance instead of per
|
||||
|
@ -170,8 +173,8 @@ class X64Backend : public Backend {
|
|||
reinterpret_cast<intptr_t>(ctx) - sizeof(X64BackendContext));
|
||||
}
|
||||
virtual uint32_t CreateGuestTrampoline(GuestTrampolineProc proc,
|
||||
void* userdata1,
|
||||
void* userdata2, bool long_term) override;
|
||||
void* userdata1, void* userdata2,
|
||||
bool long_term) override;
|
||||
|
||||
virtual void FreeGuestTrampoline(uint32_t trampoline_addr) override;
|
||||
virtual void SetGuestRoundingMode(void* ctx, unsigned int mode) override;
|
||||
|
@ -213,6 +216,7 @@ class X64Backend : public Backend {
|
|||
void* vrsqrtefp_vector_helper = nullptr;
|
||||
void* vrsqrtefp_scalar_helper = nullptr;
|
||||
void* frsqrtefp_helper = nullptr;
|
||||
|
||||
private:
|
||||
#if XE_X64_PROFILER_AVAILABLE == 1
|
||||
GuestProfilerData profiler_data_;
|
||||
|
|
|
@ -93,7 +93,8 @@ class X64CodeCache : public CodeCache {
|
|||
// This is picked to be high enough to cover whatever we can reasonably
|
||||
// expect. If we hit issues with this it probably means some corner case
|
||||
// in analysis triggering.
|
||||
//chrispy: raised this, some games that were compiled with low optimization levels can exceed this
|
||||
// chrispy: raised this, some games that were compiled with low optimization
|
||||
// levels can exceed this
|
||||
static const size_t kMaximumFunctionCount = 1000000;
|
||||
|
||||
struct UnwindReservation {
|
||||
|
|
|
@ -213,7 +213,8 @@ Win32X64CodeCache::RequestUnwindReservation(uint8_t* entry_address) {
|
|||
if (unwind_table_count_ >= kMaximumFunctionCount) {
|
||||
// we should not just be ignoring this in release if it happens
|
||||
xe::FatalError(
|
||||
"Unwind table count (unwind_table_count_) exceeded maximum! Please report this to "
|
||||
"Unwind table count (unwind_table_count_) exceeded maximum! Please "
|
||||
"report this to "
|
||||
"Xenia/Canary developers");
|
||||
}
|
||||
#else
|
||||
|
|
|
@ -210,13 +210,15 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
|||
// Adding or changing anything here must be matched!
|
||||
|
||||
/*
|
||||
pick a page to use as the local base as close to the commonly accessed page that contains most backend fields
|
||||
the sizes that are checked are chosen based on PTE coalescing sizes. zen does 16k or 32k
|
||||
pick a page to use as the local base as close to the commonly accessed page
|
||||
that contains most backend fields the sizes that are checked are chosen
|
||||
based on PTE coalescing sizes. zen does 16k or 32k
|
||||
*/
|
||||
size_t stack_size = StackLayout::GUEST_STACK_SIZE;
|
||||
if (stack_offset < (4096 - sizeof(X64BackendContext))) {
|
||||
locals_page_delta_ = 4096;
|
||||
} else if (stack_offset < (16384 - sizeof(X64BackendContext))) {//16k PTE coalescing
|
||||
} else if (stack_offset <
|
||||
(16384 - sizeof(X64BackendContext))) { // 16k PTE coalescing
|
||||
locals_page_delta_ = 16384;
|
||||
} else if (stack_offset < (32768 - sizeof(X64BackendContext))) {
|
||||
locals_page_delta_ = 32768;
|
||||
|
@ -224,7 +226,8 @@ bool X64Emitter::Emit(HIRBuilder* builder, EmitFunctionInfo& func_info) {
|
|||
locals_page_delta_ = 65536;
|
||||
} else {
|
||||
// extremely unlikely, fall back to stack
|
||||
stack_size = xe::align<size_t>(StackLayout::GUEST_STACK_SIZE + stack_offset, 16);
|
||||
stack_size =
|
||||
xe::align<size_t>(StackLayout::GUEST_STACK_SIZE + stack_offset, 16);
|
||||
locals_page_delta_ = 0;
|
||||
}
|
||||
|
||||
|
@ -1267,12 +1270,13 @@ uintptr_t X64Emitter::PlaceConstData() {
|
|||
|
||||
std::memcpy(mem, xmm_consts, sizeof(xmm_consts));
|
||||
/*
|
||||
set each 32-bit element of the constant XMMVRsqrteTableBase to be the address of the start of the constant XMMVRsqrteTableStart
|
||||
this
|
||||
set each 32-bit element of the constant XMMVRsqrteTableBase to be the
|
||||
address of the start of the constant XMMVRsqrteTableStart this
|
||||
*/
|
||||
vec128_t* deferred_constants = reinterpret_cast<vec128_t*>(mem);
|
||||
vec128_t* vrsqrte_table_base = &deferred_constants[XMMVRsqrteTableBase];
|
||||
uint32_t ptr_to_vrsqrte_table32 = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&deferred_constants[XMMVRsqrteTableStart]));
|
||||
uint32_t ptr_to_vrsqrte_table32 = static_cast<uint32_t>(
|
||||
reinterpret_cast<uintptr_t>(&deferred_constants[XMMVRsqrteTableStart]));
|
||||
*vrsqrte_table_base = vec128i(ptr_to_vrsqrte_table32);
|
||||
|
||||
memory::Protect(mem, kConstDataSize, memory::PageAccess::kReadOnly, nullptr);
|
||||
|
@ -1288,8 +1292,10 @@ void X64Emitter::FreeConstData(uintptr_t data) {
|
|||
Xbyak::Address X64Emitter::GetXmmConstPtr(XmmConst id) {
|
||||
// Load through fixed constant table setup by PlaceConstData.
|
||||
// It's important that the pointer is not signed, as it will be sign-extended.
|
||||
void* emitter_data_ptr = backend_->LookupXMMConstantAddress(static_cast<unsigned>(id));
|
||||
xenia_assert(reinterpret_cast<uintptr_t>(emitter_data_ptr) < (1ULL << 31));//must not have signbit set
|
||||
void* emitter_data_ptr =
|
||||
backend_->LookupXMMConstantAddress(static_cast<unsigned>(id));
|
||||
xenia_assert(reinterpret_cast<uintptr_t>(emitter_data_ptr) <
|
||||
(1ULL << 31)); // must not have signbit set
|
||||
return ptr[emitter_data_ptr];
|
||||
}
|
||||
// Implies possible StashXmm(0, ...)!
|
||||
|
|
|
@ -176,7 +176,10 @@ enum XmmConst {
|
|||
XMMVSRShlByteshuf,
|
||||
XMMVSRMask,
|
||||
XMMVRsqrteTableStart,
|
||||
XMMVRsqrteTableBase = XMMVRsqrteTableStart + (32 / 4), //32 4-byte elements in table, 4 4-byte elements fit in each xmm
|
||||
XMMVRsqrteTableBase =
|
||||
XMMVRsqrteTableStart +
|
||||
(32 /
|
||||
4), // 32 4-byte elements in table, 4 4-byte elements fit in each xmm
|
||||
|
||||
};
|
||||
using amdfx::xopcompare_e;
|
||||
|
@ -390,6 +393,7 @@ class X64Emitter : public Xbyak::CodeGenerator {
|
|||
void EmitGetCurrentThreadId();
|
||||
void EmitTraceUserCallReturn();
|
||||
static void HandleStackpointOverflowError(ppc::PPCContext* context);
|
||||
|
||||
protected:
|
||||
Processor* processor_ = nullptr;
|
||||
X64Backend* backend_ = nullptr;
|
||||
|
|
|
@ -398,8 +398,7 @@ struct I<OPCODE, DEST, SRC1, SRC2, SRC3> : DestField<DEST> {
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
XE_MAYBE_UNUSED
|
||||
static const T GetTempReg(X64Emitter& e);
|
||||
XE_MAYBE_UNUSED static const T GetTempReg(X64Emitter& e);
|
||||
template <>
|
||||
XE_MAYBE_UNUSED const Reg8 GetTempReg<Reg8>(X64Emitter& e) {
|
||||
return e.al;
|
||||
|
|
|
@ -705,7 +705,8 @@ struct STORE_LOCAL_I16
|
|||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// e.TraceStoreI16(DATA_LOCAL, i.src1.constant, i.src2);
|
||||
if (LocalStoreMayUseMembaseLow(e, i)) {
|
||||
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()], e.GetMembaseReg().cvt16());
|
||||
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()],
|
||||
e.GetMembaseReg().cvt16());
|
||||
} else {
|
||||
e.mov(e.word[e.GetLocalsBase() + i.src1.constant()], i.src2);
|
||||
}
|
||||
|
@ -716,7 +717,8 @@ struct STORE_LOCAL_I32
|
|||
static void Emit(X64Emitter& e, const EmitArgType& i) {
|
||||
// e.TraceStoreI32(DATA_LOCAL, i.src1.constant, i.src2);
|
||||
if (LocalStoreMayUseMembaseLow(e, i)) {
|
||||
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()], e.GetMembaseReg().cvt32());
|
||||
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()],
|
||||
e.GetMembaseReg().cvt32());
|
||||
} else {
|
||||
e.mov(e.dword[e.GetLocalsBase() + i.src1.constant()], i.src2);
|
||||
}
|
||||
|
|
|
@ -2120,9 +2120,9 @@ struct RSQRT_V128 : Sequence<RSQRT_V128, I<OPCODE_RSQRT, V128Op, V128Op>> {
|
|||
e.ChangeMxcsrMode(MXCSRMode::Vmx);
|
||||
Xmm src1 = GetInputRegOrConstant(e, i.src1, e.xmm3);
|
||||
/*
|
||||
the vast majority of inputs to vrsqrte come from vmsum3 or vmsum4 as part
|
||||
of a vector normalization sequence. in fact, its difficult to find uses of vrsqrte in titles
|
||||
that have inputs which do not come from vmsum.
|
||||
the vast majority of inputs to vrsqrte come from vmsum3 or vmsum4 as
|
||||
part of a vector normalization sequence. in fact, its difficult to find
|
||||
uses of vrsqrte in titles that have inputs which do not come from vmsum.
|
||||
*/
|
||||
if (i.src1.value && i.src1.value->AllFloatVectorLanesSameValue()) {
|
||||
e.vmovss(e.xmm0, src1);
|
||||
|
@ -3193,8 +3193,7 @@ struct SET_ROUNDING_MODE_I32
|
|||
|
||||
if (constant_value & 4) {
|
||||
e.or_(flags_ptr, 1U << kX64BackendNonIEEEMode);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
e.btr(flags_ptr, kX64BackendNonIEEEMode);
|
||||
}
|
||||
e.mov(e.dword[e.rsp + StackLayout::GUEST_SCRATCH], e.eax);
|
||||
|
|
|
@ -122,10 +122,12 @@ class StackLayout {
|
|||
*
|
||||
*/
|
||||
static const size_t GUEST_STACK_SIZE = 104;
|
||||
//was GUEST_CTX_HOME, can't remove because that'd throw stack alignment off. instead, can be used as a temporary in sequences
|
||||
// was GUEST_CTX_HOME, can't remove because that'd throw stack alignment off.
|
||||
// instead, can be used as a temporary in sequences
|
||||
static const size_t GUEST_SCRATCH = 0;
|
||||
|
||||
//when profiling is on, this stores the nanosecond time at the start of the function
|
||||
// when profiling is on, this stores the nanosecond time at the start of the
|
||||
// function
|
||||
static const size_t GUEST_PROFILER_START = 80;
|
||||
static const size_t GUEST_RET_ADDR = 88;
|
||||
static const size_t GUEST_CALL_RET_ADDR = 96;
|
||||
|
|
|
@ -29,8 +29,7 @@ namespace x64 {
|
|||
|
||||
bool trace_enabled = true;
|
||||
|
||||
#define THREAD_MATCH \
|
||||
(!TARGET_THREAD || ppc_context->thread_id == TARGET_THREAD)
|
||||
#define THREAD_MATCH (!TARGET_THREAD || ppc_context->thread_id == TARGET_THREAD)
|
||||
#define IFLUSH()
|
||||
#define IPRINT(s) \
|
||||
if (trace_enabled && THREAD_MATCH) \
|
||||
|
|
|
@ -1372,9 +1372,10 @@ bool SimplificationPass::SimplifyVectorOps(hir::Instr* i,
|
|||
}
|
||||
|
||||
/*
|
||||
splatting a 32-bit value extracted from a vector where all 4 32-bit values are the same should be eliminated and
|
||||
instead use the vector extracted from, which will be identical
|
||||
have seen this happen, some games vmsum and then splat the low float to all 4 floats, even though it already is there
|
||||
splatting a 32-bit value extracted from a vector where all 4 32-bit values
|
||||
are the same should be eliminated and instead use the vector extracted from,
|
||||
which will be identical have seen this happen, some games vmsum and then
|
||||
splat the low float to all 4 floats, even though it already is there
|
||||
*/
|
||||
if (opc == OPCODE_SPLAT) {
|
||||
if (i->dest->type == VEC128_TYPE) {
|
||||
|
@ -1388,7 +1389,6 @@ bool SimplificationPass::SimplifyVectorOps(hir::Instr* i,
|
|||
if (defining_opcode == OPCODE_EXTRACT) {
|
||||
auto value_extracted_from = splat_input_definition->src1.value;
|
||||
if (value_extracted_from->type == VEC128_TYPE) {
|
||||
|
||||
xenia_assert(splat_input_definition->dest->type == splat_type);
|
||||
|
||||
if (value_extracted_from->AllFloatVectorLanesSameValue()) {
|
||||
|
|
|
@ -95,7 +95,6 @@ class Export {
|
|||
uint32_t variable_ptr;
|
||||
|
||||
struct {
|
||||
|
||||
// Trampoline that is called from the guest-to-host thunk.
|
||||
// Expects only PPC context as first arg.
|
||||
ExportTrampoline trampoline;
|
||||
|
|
|
@ -115,7 +115,6 @@ uintptr_t GuestFunction::MapGuestAddressToMachineCode(
|
|||
return reinterpret_cast<uintptr_t>(machine_code()) + entry->code_offset;
|
||||
} else {
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,10 +79,11 @@ class Instr {
|
|||
void MoveBefore(Instr* other);
|
||||
void Replace(const OpcodeInfo* new_opcode, uint16_t new_flags);
|
||||
void UnlinkAndNOP();
|
||||
//chrispy: wanted to change this one to Remove, but i changed Remove's name to UnlinkAndNOP,
|
||||
//so if changes happened in master that we wanted to port over, and those changes used Remove, then we would have a lot of issues that the cause of would
|
||||
//be difficult to track
|
||||
//^todo: rework this comment, im frazzled
|
||||
// chrispy: wanted to change this one to Remove, but i changed Remove's name
|
||||
// to UnlinkAndNOP, so if changes happened in master that we wanted to port
|
||||
// over, and those changes used Remove, then we would have a lot of issues
|
||||
// that the cause of would be difficult to track ^todo: rework this comment,
|
||||
// im frazzled
|
||||
void Deallocate();
|
||||
const OpcodeInfo* GetOpcodeInfo() const { return opcode; }
|
||||
// if opcode is null, we have bigger problems
|
||||
|
|
|
@ -30,9 +30,7 @@ class Label {
|
|||
// this will later be used as an input to xbyak. xbyak only accepts
|
||||
// std::string as a value, not passed by reference, so precomputing the
|
||||
// stringification does not help
|
||||
std::string GetIdString() {
|
||||
return std::to_string(id);
|
||||
}
|
||||
std::string GetIdString() { return std::to_string(id); }
|
||||
};
|
||||
|
||||
} // namespace hir
|
||||
|
|
|
@ -1819,7 +1819,8 @@ re_enter:
|
|||
xenia_assert(for_value->IsConstant());
|
||||
|
||||
auto&& constant_value = for_value->constant.v128;
|
||||
for (unsigned constant_lane_index = 1; constant_lane_index < 4; ++constant_lane_index) {
|
||||
for (unsigned constant_lane_index = 1; constant_lane_index < 4;
|
||||
++constant_lane_index) {
|
||||
if (constant_value.u32[0] != constant_value.u32[constant_lane_index]) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1845,8 +1846,9 @@ re_enter:
|
|||
return true;
|
||||
}
|
||||
// if splat of 32-bit value type, return true
|
||||
//technically a splat of int16 or int8 would also produce the same "float" in all lanes
|
||||
//but i think its best to keep this function focused on specifically float data
|
||||
// technically a splat of int16 or int8 would also produce the same "float" in
|
||||
// all lanes but i think its best to keep this function focused on
|
||||
// specifically float data
|
||||
if (definition_opcode_number == OPCODE_SPLAT) {
|
||||
if (definition->dest->type == VEC128_TYPE) {
|
||||
auto splat_src_value_type = definition->src1.value->type;
|
||||
|
@ -1883,7 +1885,6 @@ re_enter:
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace hir
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
|
|
@ -621,6 +621,7 @@ class Value {
|
|||
bool AllFloatVectorLanesSameValue() const {
|
||||
return Value::AllFloatVectorLanesSameValue(this);
|
||||
}
|
||||
|
||||
private:
|
||||
/*
|
||||
returns true if for_value (which must be VEC128_TYPE) has the same value in
|
||||
|
|
|
@ -48,7 +48,9 @@ class MMIOHandler {
|
|||
typedef uint32_t (*HostToGuestVirtual)(const void* context,
|
||||
const void* host_address);
|
||||
typedef bool (*AccessViolationCallback)(
|
||||
global_unique_lock_type global_lock_locked_once, //not passed by reference with const like the others?
|
||||
global_unique_lock_type
|
||||
global_lock_locked_once, // not passed by reference with const like
|
||||
// the others?
|
||||
void* context, void* host_address, bool is_write);
|
||||
|
||||
// access_violation_callback is called with global_critical_region locked once
|
||||
|
|
|
@ -55,6 +55,7 @@ class Module {
|
|||
bool ReadMap(const char* file_name);
|
||||
|
||||
virtual void Precompile() {}
|
||||
|
||||
protected:
|
||||
virtual std::unique_ptr<Function> CreateFunction(uint32_t address) = 0;
|
||||
|
||||
|
|
|
@ -789,7 +789,6 @@ int InstrEmit_mtspr(PPCHIRBuilder& f, const InstrData& i) {
|
|||
// code requires it. Sequences of mtmsr/lwar/stcw/mtmsr come up a lot, and
|
||||
// without the lock here threads can livelock.
|
||||
|
||||
|
||||
// 0x400 = debug singlestep i think
|
||||
// ive seen 0x8000 used in kernel code
|
||||
int InstrEmit_mfmsr(PPCHIRBuilder& f, const InstrData& i) {
|
||||
|
|
|
@ -106,11 +106,12 @@ bool PPCFrontend::Initialize() {
|
|||
}
|
||||
|
||||
bool PPCFrontend::DeclareFunction(GuestFunction* function) {
|
||||
|
||||
//chrispy: make sure we aren't declaring a function that is actually padding data, this will mess up PPCScanner and is hard to debug
|
||||
//wow, this halo reach actually has branches into 0 opcodes, look into further
|
||||
// chrispy: make sure we aren't declaring a function that is actually padding
|
||||
// data, this will mess up PPCScanner and is hard to debug wow, this halo
|
||||
// reach actually has branches into 0 opcodes, look into further
|
||||
// xenia_assert(*reinterpret_cast<const uint32_t*>(
|
||||
// this->memory()->TranslateVirtual(function->address())) != 0);
|
||||
// this->memory()->TranslateVirtual(function->address())) !=
|
||||
// 0);
|
||||
// Could scan or something here.
|
||||
// Could also check to see if it's a well-known function type and classify
|
||||
// for later.
|
||||
|
|
|
@ -80,8 +80,10 @@ class PPCHIRBuilder : public hir::HIRBuilder {
|
|||
|
||||
void StoreReserved(Value* val);
|
||||
Value* LoadReserved();
|
||||
//calls original impl in hirbuilder, but also records the is_return_site bit into flags in the guestmodule
|
||||
// calls original impl in hirbuilder, but also records the is_return_site bit
|
||||
// into flags in the guestmodule
|
||||
void SetReturnAddress(Value* value);
|
||||
|
||||
private:
|
||||
void MaybeBreakOnInstruction(uint32_t address);
|
||||
void AnnotateLabel(uint32_t address, Label* label);
|
||||
|
|
|
@ -1326,8 +1326,8 @@ uint32_t Processor::GuestAtomicDecrement32(ppc::PPCContext* context,
|
|||
|
||||
uint32_t Processor::GuestAtomicOr32(ppc::PPCContext* context,
|
||||
uint32_t guest_address, uint32_t mask) {
|
||||
return xe::byte_swap(xe::atomic_or(
|
||||
context->TranslateVirtual<volatile int32_t*>(guest_address),
|
||||
return xe::byte_swap(
|
||||
xe::atomic_or(context->TranslateVirtual<volatile int32_t*>(guest_address),
|
||||
xe::byte_swap(mask)));
|
||||
}
|
||||
uint32_t Processor::GuestAtomicXor32(ppc::PPCContext* context,
|
||||
|
|
|
@ -77,8 +77,7 @@ ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
|
|||
|
||||
// Allocate with 64b alignment.
|
||||
|
||||
context_ = reinterpret_cast<ppc::PPCContext*>(
|
||||
AllocateContext());
|
||||
context_ = reinterpret_cast<ppc::PPCContext*>(AllocateContext());
|
||||
processor->backend()->InitializeBackendContext(context_);
|
||||
assert_true(((uint64_t)context_ & 0x3F) == 0);
|
||||
std::memset(context_, 0, sizeof(ppc::PPCContext));
|
||||
|
@ -98,8 +97,10 @@ ThreadState::ThreadState(Processor* processor, uint32_t thread_id,
|
|||
context_->msr = 0x9030; // dumped from a real 360, 0x8000
|
||||
|
||||
// this register can be used for arbitrary data according to the PPC docs
|
||||
//but the suggested use is to mark which vector registers are in use, for faster save/restore
|
||||
//it seems unlikely anything uses this, especially since we have way more than 32 vrs, but setting it to all ones seems closer to correct than 0
|
||||
// but the suggested use is to mark which vector registers are in use, for
|
||||
// faster save/restore it seems unlikely anything uses this, especially since
|
||||
// we have way more than 32 vrs, but setting it to all ones seems closer to
|
||||
// correct than 0
|
||||
context_->vrsave = ~0u;
|
||||
}
|
||||
|
||||
|
|
|
@ -230,9 +230,7 @@ class Emulator {
|
|||
xe::Delegate<> on_exit;
|
||||
|
||||
private:
|
||||
enum : uint64_t {
|
||||
EmulatorFlagDisclaimerAcknowledged = 1ULL << 0
|
||||
};
|
||||
enum : uint64_t { EmulatorFlagDisclaimerAcknowledged = 1ULL << 0 };
|
||||
static uint64_t GetPersistentEmulatorFlags();
|
||||
static void SetPersistentEmulatorFlags(uint64_t new_flags);
|
||||
static std::string CanonicalizeFileExtension(
|
||||
|
|
|
@ -100,11 +100,14 @@ bool CommandProcessor::Initialize() {
|
|||
}
|
||||
|
||||
worker_running_ = true;
|
||||
worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
||||
new kernel::XHostThread(kernel_state_, 128 * 1024, 0, [this]() {
|
||||
worker_thread_ =
|
||||
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||
kernel_state_, 128 * 1024, 0,
|
||||
[this]() {
|
||||
WorkerThreadMain();
|
||||
return 0;
|
||||
}, kernel_state_->GetIdleProcess()));
|
||||
},
|
||||
kernel_state_->GetIdleProcess()));
|
||||
worker_thread_->set_name("GPU Commands");
|
||||
worker_thread_->Create();
|
||||
|
||||
|
@ -270,7 +273,8 @@ void CommandProcessor::WorkerThreadMain() {
|
|||
|
||||
// TODO(benvanik): use reader->Read_update_freq_ and only issue after moving
|
||||
// that many indices.
|
||||
// Keep in mind that the gpu also updates the cpu-side copy if the write pointer and read pointer would be equal
|
||||
// Keep in mind that the gpu also updates the cpu-side copy if the write
|
||||
// pointer and read pointer would be equal
|
||||
if (read_ptr_writeback_ptr_) {
|
||||
xe::store_and_swap<uint32_t>(
|
||||
memory_->TranslatePhysical(read_ptr_writeback_ptr_), read_ptr_index_);
|
||||
|
@ -360,9 +364,8 @@ void CommandProcessor::EnableReadPointerWriteBack(uint32_t ptr,
|
|||
XE_NOINLINE XE_COLD void CommandProcessor::LogKickoffInitator(uint32_t value) {
|
||||
cpu::backend::GuestPseudoStackTrace st;
|
||||
|
||||
if (logging::internal::ShouldLog(LogLevel::Debug) && kernel_state_->processor()
|
||||
->backend()
|
||||
->PopulatePseudoStacktrace(&st)) {
|
||||
if (logging::internal::ShouldLog(LogLevel::Debug) &&
|
||||
kernel_state_->processor()->backend()->PopulatePseudoStacktrace(&st)) {
|
||||
logging::LoggerBatch<LogLevel::Debug> log_initiator{};
|
||||
|
||||
log_initiator("Updating read ptr to {}, initiator stacktrace below\n",
|
||||
|
@ -390,7 +393,8 @@ void CommandProcessor::UpdateWritePointer(uint32_t value) {
|
|||
|
||||
void CommandProcessor::LogRegisterSet(uint32_t register_index, uint32_t value) {
|
||||
#if XE_ENABLE_GPU_REG_WRITE_LOGGING == 1
|
||||
if (cvars::log_guest_driven_gpu_register_written_values && logging::internal::ShouldLog(LogLevel::Debug)) {
|
||||
if (cvars::log_guest_driven_gpu_register_written_values &&
|
||||
logging::internal::ShouldLog(LogLevel::Debug)) {
|
||||
const RegisterInfo* reginfo = RegisterFile::GetRegisterInfo(register_index);
|
||||
|
||||
if (!reginfo) {
|
||||
|
@ -734,7 +738,6 @@ void CommandProcessor::PrepareForWait() { trace_writer_.Flush(); }
|
|||
|
||||
void CommandProcessor::ReturnFromWait() {}
|
||||
|
||||
|
||||
void CommandProcessor::InitializeTrace() {
|
||||
// Write the initial register values, to be loaded directly into the
|
||||
// RegisterFile since all registers, including those that may have side
|
||||
|
|
|
@ -225,7 +225,6 @@ class CommandProcessor {
|
|||
virtual void PrepareForWait();
|
||||
virtual void ReturnFromWait();
|
||||
|
||||
|
||||
virtual void OnPrimaryBufferEnd() {}
|
||||
|
||||
#include "pm4_command_processor_declare.h"
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
||||
#include "xenia/gpu/draw_util.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/packet_disassembler.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/gpu/packet_disassembler.h"
|
||||
#include "xenia/ui/d3d12/d3d12_presenter.h"
|
||||
#include "xenia/ui/d3d12/d3d12_util.h"
|
||||
|
||||
|
@ -65,7 +65,6 @@ void D3D12SaveGPUSetting(D3D12GPUSetting setting, uint64_t value) {
|
|||
|
||||
namespace d3d12 {
|
||||
|
||||
|
||||
// Generated with `xb buildshaders`.
|
||||
namespace shaders {
|
||||
#include "xenia/gpu/shaders/bytecode/d3d12_5_1/apply_gamma_pwl_cs.h"
|
||||
|
@ -4992,7 +4991,8 @@ bool D3D12CommandProcessor::UpdateBindings_BindfulPath(
|
|||
}
|
||||
// Null SRV + UAV + EDRAM.
|
||||
gpu_handle_shared_memory_uav_and_edram_ = view_gpu_handle;
|
||||
ui::d3d12::util::CreateBufferRawSRV(provider.GetDevice(), view_cpu_handle, nullptr, 0);
|
||||
ui::d3d12::util::CreateBufferRawSRV(provider.GetDevice(), view_cpu_handle,
|
||||
nullptr, 0);
|
||||
view_cpu_handle.ptr += descriptor_size_view;
|
||||
view_gpu_handle.ptr += descriptor_size_view;
|
||||
shared_memory_->WriteRawUAVDescriptor(view_cpu_handle);
|
||||
|
|
|
@ -245,7 +245,8 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
|||
void WriteFetchFromMem(uint32_t start_index, uint32_t* base,
|
||||
uint32_t num_registers);
|
||||
|
||||
void WritePossiblySpecialRegistersFromMem(uint32_t start_index, uint32_t* base,
|
||||
void WritePossiblySpecialRegistersFromMem(uint32_t start_index,
|
||||
uint32_t* base,
|
||||
uint32_t num_registers);
|
||||
template <uint32_t register_lower_bound, uint32_t register_upper_bound>
|
||||
XE_FORCEINLINE void WriteRegisterRangeFromMem_WithKnownBound(
|
||||
|
@ -262,8 +263,7 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
|||
uint32_t base,
|
||||
uint32_t num_registers);
|
||||
XE_NOINLINE
|
||||
void WriteOneRegisterFromRing(uint32_t base,
|
||||
uint32_t num_times);
|
||||
void WriteOneRegisterFromRing(uint32_t base, uint32_t num_times);
|
||||
|
||||
XE_FORCEINLINE
|
||||
void WriteALURangeFromRing(xe::RingBuffer* ring, uint32_t base,
|
||||
|
@ -795,7 +795,6 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
|||
draw_util::GetViewportInfoArgs previous_viewport_info_args_;
|
||||
draw_util::ViewportInfo previous_viewport_info_;
|
||||
|
||||
|
||||
std::atomic<bool> pix_capture_requested_ = false;
|
||||
bool pix_capturing_;
|
||||
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/literals.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace d3d12 {
|
||||
|
@ -32,7 +32,8 @@ class DeferredCommandList {
|
|||
public:
|
||||
static constexpr size_t MAX_SIZEOF_COMMANDLIST = 65536 * 128; // around 8 mb
|
||||
/*
|
||||
chrispy: upped from 1_MiB to 4_MiB, m:durandal hits frequent resizes in large open maps
|
||||
chrispy: upped from 1_MiB to 4_MiB, m:durandal hits frequent resizes in
|
||||
large open maps
|
||||
*/
|
||||
DeferredCommandList(const D3D12CommandProcessor& command_processor,
|
||||
size_t initial_size_bytes = MAX_SIZEOF_COMMANDLIST);
|
||||
|
|
|
@ -552,8 +552,7 @@ void GetHostViewportInfo(GetViewportInfoArgs* XE_RESTRICT args,
|
|||
}
|
||||
}
|
||||
template <bool clamp_to_surface_pitch>
|
||||
static inline
|
||||
void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
||||
static inline void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
||||
Scissor& XE_RESTRICT scissor_out) {
|
||||
#if XE_ARCH_AMD64 == 1
|
||||
auto pa_sc_window_scissor_tl = regs.Get<reg::PA_SC_WINDOW_SCISSOR_TL>();
|
||||
|
@ -623,8 +622,7 @@ void GetScissorTmpl(const RegisterFile& XE_RESTRICT regs,
|
|||
// interlock-based custom RB implementations) and using conventional render
|
||||
// targets, but padded to EDRAM tiles.
|
||||
tmp1 = _mm_blend_epi16(
|
||||
tmp1, _mm_min_epi32(tmp1, _mm_set1_epi32(surface_pitch)),
|
||||
0b00110011);
|
||||
tmp1, _mm_min_epi32(tmp1, _mm_set1_epi32(surface_pitch)), 0b00110011);
|
||||
}
|
||||
|
||||
tmp1 = _mm_max_epi32(tmp1, _mm_setzero_si128());
|
||||
|
|
|
@ -25,10 +25,10 @@
|
|||
#include "xenia/base/threading.h"
|
||||
#include "xenia/gpu/command_processor.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/ui/graphics_provider.h"
|
||||
#include "xenia/ui/window.h"
|
||||
#include "xenia/ui/windowed_app_context.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
DEFINE_bool(
|
||||
store_shaders, true,
|
||||
"Store shaders persistently and load them when loading games to avoid "
|
||||
|
@ -102,8 +102,10 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
|||
|
||||
// 60hz vsync timer.
|
||||
vsync_worker_running_ = true;
|
||||
vsync_worker_thread_ = kernel::object_ref<kernel::XHostThread>(
|
||||
new kernel::XHostThread(kernel_state_, 128 * 1024, 0, [this]() {
|
||||
vsync_worker_thread_ =
|
||||
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||
kernel_state_, 128 * 1024, 0,
|
||||
[this]() {
|
||||
const double vsync_duration_d =
|
||||
cvars::vsync
|
||||
? std::max<double>(
|
||||
|
@ -117,13 +119,15 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
|||
const uint64_t current_time = Clock::QueryGuestTickCount();
|
||||
const uint64_t tick_freq = Clock::guest_tick_frequency();
|
||||
const uint64_t time_delta = current_time - last_frame_time;
|
||||
const double elapsed_d = static_cast<double>(time_delta) /
|
||||
const double elapsed_d =
|
||||
static_cast<double>(time_delta) /
|
||||
(static_cast<double>(tick_freq) / 1000.0);
|
||||
if (elapsed_d >= vsync_duration_d) {
|
||||
last_frame_time = current_time;
|
||||
|
||||
// TODO(disjtqz): should recalculate the remaining time to a vblank
|
||||
// after MarkVblank, no idea how long the guest code normally takes
|
||||
// TODO(disjtqz): should recalculate the remaining time to a
|
||||
// vblank after MarkVblank, no idea how long the guest code
|
||||
// normally takes
|
||||
MarkVblank();
|
||||
if (cvars::vsync) {
|
||||
const uint64_t estimated_nanoseconds = static_cast<uint64_t>(
|
||||
|
@ -138,7 +142,8 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
|||
}
|
||||
}
|
||||
return 0;
|
||||
}, kernel_state->GetIdleProcess()));
|
||||
},
|
||||
kernel_state->GetIdleProcess()));
|
||||
// As we run vblank interrupts the debugger must be able to suspend us.
|
||||
vsync_worker_thread_->set_can_debugger_suspend(true);
|
||||
vsync_worker_thread_->set_name("GPU VSync");
|
||||
|
@ -267,7 +272,8 @@ void GraphicsSystem::SetInterruptCallback(uint32_t callback,
|
|||
}
|
||||
|
||||
void GraphicsSystem::DispatchInterruptCallback(uint32_t source, uint32_t cpu) {
|
||||
kernel_state()->EmulateCPInterruptDPC(interrupt_callback_,interrupt_callback_data_, source, cpu);
|
||||
kernel_state()->EmulateCPInterruptDPC(interrupt_callback_,
|
||||
interrupt_callback_data_, source, cpu);
|
||||
}
|
||||
|
||||
void GraphicsSystem::MarkVblank() {
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
#else
|
||||
#define PM4_OVERRIDE
|
||||
#endif
|
||||
void ExecuteIndirectBuffer(uint32_t ptr,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
void ExecuteIndirectBuffer(uint32_t ptr, uint32_t count) XE_RESTRICT;
|
||||
virtual uint32_t ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index)
|
||||
XE_RESTRICT PM4_OVERRIDE;
|
||||
virtual bool ExecutePacket() PM4_OVERRIDE;
|
||||
|
@ -26,16 +25,12 @@ bool ExecutePacketType2( uint32_t packet) XE_RESTRICT;
|
|||
XE_NOINLINE
|
||||
bool ExecutePacketType3(uint32_t packet) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType3_ME_INIT( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_NOP( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_ME_INIT(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_NOP(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType3_INTERRUPT( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_INTERRUPT(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType3_XE_SWAP( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_XE_SWAP(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_INDIRECT_BUFFER(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
@ -43,17 +38,13 @@ XE_NOINLINE
|
|||
bool ExecutePacketType3_WAIT_REG_MEM(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType3_REG_RMW( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_REG_RMW(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_REG_TO_MEM( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_REG_TO_MEM(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType3_MEM_WRITE( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_MEM_WRITE(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType3_COND_WRITE( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_COND_WRITE(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_EVENT_WRITE(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
@ -67,13 +58,11 @@ XE_NOINLINE
|
|||
bool ExecutePacketType3_EVENT_WRITE_ZPD(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3Draw( uint32_t packet,
|
||||
const char* opcode_name,
|
||||
bool ExecutePacketType3Draw(uint32_t packet, const char* opcode_name,
|
||||
uint32_t viz_query_condition,
|
||||
uint32_t count_remaining) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_DRAW_INDX( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_DRAW_INDX(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_DRAW_INDX_2(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
@ -87,12 +76,10 @@ XE_FORCEINLINE
|
|||
bool ExecutePacketType3_LOAD_ALU_CONSTANT(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_SET_SHADER_CONSTANTS(
|
||||
uint32_t packet,
|
||||
bool ExecutePacketType3_SET_SHADER_CONSTANTS(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_IM_LOAD( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
bool ExecutePacketType3_IM_LOAD(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_IM_LOAD_IMMEDIATE(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
@ -100,9 +87,7 @@ bool ExecutePacketType3_IM_LOAD_IMMEDIATE( uint32_t packet,
|
|||
bool ExecutePacketType3_INVALIDATE_STATE(uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_VIZ_QUERY( uint32_t packet,
|
||||
uint32_t count) XE_RESTRICT;
|
||||
|
||||
bool ExecutePacketType3_VIZ_QUERY(uint32_t packet, uint32_t count) XE_RESTRICT;
|
||||
|
||||
XE_FORCEINLINE
|
||||
void WriteEventInitiator(uint32_t value) XE_RESTRICT;
|
||||
|
|
|
@ -683,11 +683,10 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_INDIRECT_BUFFER(
|
|||
to 54 bytes
|
||||
*/
|
||||
static bool MatchValueAndRef(uint32_t value, uint32_t ref, uint32_t wait_info) {
|
||||
// smaller code is generated than the #else path, although whether it is faster
|
||||
// i do not know. i don't think games do an enormous number of cond_write
|
||||
// though, so we have picked
|
||||
// the path with the smaller codegen.
|
||||
// we do technically have more instructions executed vs the switch case method,
|
||||
// smaller code is generated than the #else path, although whether it is
|
||||
// faster i do not know. i don't think games do an enormous number of
|
||||
// cond_write though, so we have picked the path with the smaller codegen. we
|
||||
// do technically have more instructions executed vs the switch case method,
|
||||
// but we have no mispredicts and most of our instructions are 0.25/0.3
|
||||
// throughput
|
||||
return ((((value < ref) << 1) | ((value <= ref) << 2) |
|
||||
|
@ -899,12 +898,16 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_EVENT_WRITE_SHD(
|
|||
data_value = GpuSwap(data_value, endianness);
|
||||
uint8_t* write_destination = memory_->TranslatePhysical(address);
|
||||
if (address > 0x1FFFFFFF) {
|
||||
uint32_t writeback_base = register_file_->values[XE_GPU_REG_WRITEBACK_BASE].u32;
|
||||
uint32_t writeback_size = register_file_->values[XE_GPU_REG_WRITEBACK_SIZE].u32;
|
||||
uint32_t writeback_base =
|
||||
register_file_->values[XE_GPU_REG_WRITEBACK_BASE].u32;
|
||||
uint32_t writeback_size =
|
||||
register_file_->values[XE_GPU_REG_WRITEBACK_SIZE].u32;
|
||||
uint32_t writeback_offset = address - writeback_base;
|
||||
//check whether the guest has written writeback base. if they haven't, skip the offset check
|
||||
// check whether the guest has written writeback base. if they haven't, skip
|
||||
// the offset check
|
||||
if (writeback_base != 0 && writeback_offset < writeback_size) {
|
||||
write_destination = memory_->TranslateVirtual(0x7F000000 + writeback_offset);
|
||||
write_destination =
|
||||
memory_->TranslateVirtual(0x7F000000 + writeback_offset);
|
||||
}
|
||||
}
|
||||
xe::store(write_destination, data_value);
|
||||
|
|
|
@ -883,8 +883,7 @@ class PrimitiveProcessor {
|
|||
// Must be called in a global critical region.
|
||||
void UpdateCacheBucketsNonEmptyL2(
|
||||
uint32_t bucket_index_div_64,
|
||||
[[maybe_unused]] const global_unique_lock_type&
|
||||
global_lock) {
|
||||
[[maybe_unused]] const global_unique_lock_type& global_lock) {
|
||||
uint64_t& cache_buckets_non_empty_l2_ref =
|
||||
cache_buckets_non_empty_l2_[bucket_index_div_64 >> 6];
|
||||
uint64_t cache_buckets_non_empty_l2_bit = uint64_t(1)
|
||||
|
|
|
@ -36,16 +36,18 @@ XE_GPU_REGISTER(0x0398, kDword, RBBM_PERFCOUNTER0_HI)
|
|||
XE_GPU_REGISTER(0x0399, kDword, RBBM_PERFCOUNTER1_LOW)
|
||||
XE_GPU_REGISTER(0x039A, kDword, RBBM_PERFCOUNTER1_HI)
|
||||
|
||||
//XAM reads this directly and stores it to a struct, have not tracked where it goes from there
|
||||
//PM4 command PM4_MEM_WRITE_CNTR is supposed to write this to memory
|
||||
//XE_GPU_REGISTER(0x44b, kDword,CP_PROG_COUNTER )
|
||||
// XAM reads this directly and stores it to a struct, have not tracked where it
|
||||
// goes from there PM4 command PM4_MEM_WRITE_CNTR is supposed to write this to
|
||||
// memory XE_GPU_REGISTER(0x44b, kDword,CP_PROG_COUNTER )
|
||||
XE_GPU_REGISTER(0x045E, kDword, CALLBACK_ACK)
|
||||
|
||||
XE_GPU_REGISTER(0x0578, kDword, SCRATCH_REG0) // interrupt sync
|
||||
XE_GPU_REGISTER(0x0579, kDword, SCRATCH_REG1) // present interval
|
||||
XE_GPU_REGISTER(0x057A, kDword, SCRATCH_REG2)
|
||||
XE_GPU_REGISTER(0x057B, kDword, SCRATCH_REG3)
|
||||
XE_GPU_REGISTER(0x057C, kDword, SCRATCH_REG4) //originally this was named CALLBACK_ADDRESS, but that didnt make sense
|
||||
XE_GPU_REGISTER(0x057C, kDword,
|
||||
SCRATCH_REG4) // originally this was named CALLBACK_ADDRESS,
|
||||
// but that didnt make sense
|
||||
XE_GPU_REGISTER(0x057D, kDword, SCRATCH_REG5)
|
||||
XE_GPU_REGISTER(0x057E, kDword, SCRATCH_REG6)
|
||||
XE_GPU_REGISTER(0x057F, kDword, SCRATCH_REG7)
|
||||
|
@ -56,7 +58,6 @@ XE_GPU_REGISTER(0x05C8, kDword, WAIT_UNTIL)
|
|||
// seems to have a negative value while the gpu is busy
|
||||
// XE_GPU_REGISTER(0x05D0, kDword, RBBM_STATUS)
|
||||
|
||||
|
||||
// update count = 6 bit field, bits 8- 14
|
||||
// there are several other fields here, they have an unknown purpose
|
||||
// XE_GPU_REGISTER(0x704, kDword, CP_RB_CNTL)
|
||||
|
|
|
@ -231,8 +231,7 @@ class RenderTargetCache {
|
|||
: register_file_(register_file),
|
||||
draw_resolution_scale_x_(draw_resolution_scale_x),
|
||||
draw_resolution_scale_y_(draw_resolution_scale_y),
|
||||
draw_extent_estimator_(register_file, memory, trace_writer)
|
||||
{
|
||||
draw_extent_estimator_(register_file, memory, trace_writer) {
|
||||
assert_not_zero(draw_resolution_scale_x);
|
||||
assert_not_zero(draw_resolution_scale_y);
|
||||
}
|
||||
|
|
|
@ -108,7 +108,8 @@ class TextureCache {
|
|||
// generate a mask of all bits from before the first index, and xor it with
|
||||
// all bits before the last index this produces a mask covering only the
|
||||
// bits between first and last
|
||||
uint32_t res = ((1U << first_index) - 1) ^ static_cast<uint32_t>((1ULL << (last_index + 1)) - 1ULL);
|
||||
uint32_t res = ((1U << first_index) - 1) ^
|
||||
static_cast<uint32_t>((1ULL << (last_index + 1)) - 1ULL);
|
||||
// todo: check that this is right
|
||||
|
||||
texture_bindings_in_sync_ &= ~res;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/gpu/draw_util.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/packet_disassembler.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/spirv_shader_translator.h"
|
||||
|
@ -32,7 +33,6 @@
|
|||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shared_memory.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/gpu/packet_disassembler.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/ui/vulkan/vulkan_presenter.h"
|
||||
|
|
|
@ -10,10 +10,8 @@
|
|||
#ifndef XENIA_GPU_XENOS_H_
|
||||
#define XENIA_GPU_XENOS_H_
|
||||
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
|
|
@ -54,9 +54,7 @@ struct X_FILE_FS_ATTRIBUTE_INFORMATION {
|
|||
};
|
||||
static_assert_size(X_FILE_FS_ATTRIBUTE_INFORMATION, 16);
|
||||
|
||||
enum X_FILE_DEVICE_TYPE : uint32_t {
|
||||
FILE_DEVICE_UNKNOWN = 0x22
|
||||
};
|
||||
enum X_FILE_DEVICE_TYPE : uint32_t { FILE_DEVICE_UNKNOWN = 0x22 };
|
||||
|
||||
struct X_FILE_FS_DEVICE_INFORMATION {
|
||||
be<X_FILE_DEVICE_TYPE> device_type;
|
||||
|
|
|
@ -651,7 +651,8 @@ void UserModule::Dump() {
|
|||
|
||||
for (uint32_t i = 0; i < opt_alternate_title_id->count(); i++) {
|
||||
if (opt_alternate_title_id->values[i] != 0) {
|
||||
title_ids.append(fmt::format(" {:08X},", opt_alternate_title_id->values[i]));
|
||||
title_ids.append(
|
||||
fmt::format(" {:08X},", opt_alternate_title_id->values[i]));
|
||||
}
|
||||
}
|
||||
// Remove last character as it is not necessary
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
* Copyright 2023 Xenia Canary. All rights reserved. * Released under the BSD
|
||||
*license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
|
@ -95,7 +95,8 @@ bool GrowHandleTable(uint32_t table_ptr, PPCContext* context) {
|
|||
/*
|
||||
copy old bucket list contents to new, larger bucket list
|
||||
*/
|
||||
memcpy(context->TranslateVirtual(new_dynamic_buckets),
|
||||
memcpy(
|
||||
context->TranslateVirtual(new_dynamic_buckets),
|
||||
context->TranslateVirtual(table->table_dynamic_buckets),
|
||||
sizeof(uint32_t) * (new_bucket_handle_base / SIZE_PER_HANDLE_BUCKET));
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2023 Xenia Canary. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
* Copyright 2023 Xenia Canary. All rights reserved. * Released under the BSD
|
||||
*license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
|
|
|
@ -25,5 +25,5 @@ struct X_OBJECT_CREATE_INFORMATION;
|
|||
namespace xe::kernel::util {
|
||||
class NativeList;
|
||||
class ObjectTable;
|
||||
}
|
||||
} // namespace xe::kernel::util
|
||||
#endif
|
|
@ -168,8 +168,8 @@ static void XeInsertHeadList(uint32_t list_head, X_LIST_ENTRY* entry,
|
|||
template <typename VirtualTranslator>
|
||||
static void XeInsertHeadList(X_LIST_ENTRY* list_head, X_LIST_ENTRY* entry,
|
||||
VirtualTranslator context) {
|
||||
XeInsertHeadList(list_head, XeGuestList(list_head, context),
|
||||
entry, XeGuestList(entry, context), context);
|
||||
XeInsertHeadList(list_head, XeGuestList(list_head, context), entry,
|
||||
XeGuestList(entry, context), context);
|
||||
}
|
||||
|
||||
template <typename TObject, size_t EntryListOffset>
|
||||
|
@ -227,7 +227,8 @@ struct X_TYPED_LIST : public X_LIST_ENTRY {
|
|||
}
|
||||
template <typename VirtualTranslator>
|
||||
void InsertHead(TObject* entry, VirtualTranslator translator) {
|
||||
XeInsertHeadList(static_cast<X_LIST_ENTRY*>(this), ObjectListEntry(entry), translator);
|
||||
XeInsertHeadList(static_cast<X_LIST_ENTRY*>(this), ObjectListEntry(entry),
|
||||
translator);
|
||||
}
|
||||
template <typename VirtualTranslator>
|
||||
void InsertTail(TObject* entry, VirtualTranslator translator) {
|
||||
|
|
|
@ -111,8 +111,8 @@ class ObjectTable {
|
|||
|
||||
// Generic lookup
|
||||
template <>
|
||||
object_ref<XObject> ObjectTable::LookupObject<XObject>(
|
||||
X_HANDLE handle, bool already_locked);
|
||||
object_ref<XObject> ObjectTable::LookupObject<XObject>(X_HANDLE handle,
|
||||
bool already_locked);
|
||||
|
||||
} // namespace util
|
||||
} // namespace kernel
|
||||
|
|
|
@ -35,8 +35,7 @@ using PPCContext = xe::cpu::ppc::PPCContext;
|
|||
library_name, ordinals::export_name, \
|
||||
(xe::cpu::xe_kernel_export_shim_fn)export_name##_entry);
|
||||
|
||||
#define SHIM_MEM_ADDR(a) \
|
||||
((a) ? ppc_context->TranslateVirtual(a) : nullptr)
|
||||
#define SHIM_MEM_ADDR(a) ((a) ? ppc_context->TranslateVirtual(a) : nullptr)
|
||||
|
||||
#define SHIM_MEM_8(a) xe::load_and_swap<uint8_t>(SHIM_MEM_ADDR(a))
|
||||
#define SHIM_MEM_16(a) xe::load_and_swap<uint16_t>(SHIM_MEM_ADDR(a))
|
||||
|
@ -158,9 +157,8 @@ class Param {
|
|||
} else {
|
||||
uint32_t stack_ptr =
|
||||
uint32_t(init.ppc_context->r[1]) + 0x54 + (ordinal_ - 8) * 8;
|
||||
*out_value = xe::load_and_swap<V>(
|
||||
init.ppc_context->TranslateVirtual(
|
||||
stack_ptr));
|
||||
*out_value =
|
||||
xe::load_and_swap<V>(init.ppc_context->TranslateVirtual(stack_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,6 +214,7 @@ class ContextParam : public Param {
|
|||
X_KPCR* GetPCR() const { return TranslateGPR<X_KPCR*>(13); }
|
||||
|
||||
XThread* CurrentXThread() const;
|
||||
|
||||
protected:
|
||||
PPCContext* XE_RESTRICT ctx_;
|
||||
};
|
||||
|
@ -223,10 +222,7 @@ class ContextParam : public Param {
|
|||
class PointerParam : public ParamBase<uint32_t> {
|
||||
public:
|
||||
PointerParam(Init& init) : ParamBase(init) {
|
||||
host_ptr_ =
|
||||
value_
|
||||
? init.ppc_context->TranslateVirtual(value_)
|
||||
: nullptr;
|
||||
host_ptr_ = value_ ? init.ppc_context->TranslateVirtual(value_) : nullptr;
|
||||
}
|
||||
PointerParam(void* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
||||
PointerParam& operator=(void*& other) {
|
||||
|
@ -296,10 +292,7 @@ class StringPointerParam : public ParamBase<uint32_t> {
|
|||
public:
|
||||
StringPointerParam(Init& init) : ParamBase(init) {
|
||||
host_ptr_ =
|
||||
value_
|
||||
? init.ppc_context->TranslateVirtual<CHAR*>(
|
||||
value_)
|
||||
: nullptr;
|
||||
value_ ? init.ppc_context->TranslateVirtual<CHAR*>(value_) : nullptr;
|
||||
}
|
||||
StringPointerParam(CHAR* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
||||
StringPointerParam& operator=(const CHAR*& other) {
|
||||
|
@ -323,9 +316,7 @@ class TypedPointerParam : public ParamBase<uint32_t> {
|
|||
public:
|
||||
TypedPointerParam(Init& init) : ParamBase(init) {
|
||||
host_ptr_ =
|
||||
value_ ? init.ppc_context->TranslateVirtual<T*>(
|
||||
value_)
|
||||
: nullptr;
|
||||
value_ ? init.ppc_context->TranslateVirtual<T*>(value_) : nullptr;
|
||||
}
|
||||
TypedPointerParam(T* host_ptr) : ParamBase(), host_ptr_(host_ptr) {}
|
||||
TypedPointerParam& operator=(const T*& other) {
|
||||
|
|
|
@ -187,8 +187,7 @@ class XdbfWrapper {
|
|||
XdbfPropertyTableEntry GetProperty(const uint32_t id) const;
|
||||
XdbfContextTableEntry GetContext(const uint32_t id) const;
|
||||
std::vector<XdbfViewTable> GetStatsView() const;
|
||||
XdbfSharedView GetSharedView(const uint8_t* ptr,
|
||||
uint32_t& byte_count) const;
|
||||
XdbfSharedView GetSharedView(const uint8_t* ptr, uint32_t& byte_count) const;
|
||||
|
||||
void GetPropertyBagMetadata(const uint8_t* ptr, uint32_t& byte_count,
|
||||
std::vector<xe::be<uint32_t>>& contexts,
|
||||
|
@ -196,7 +195,8 @@ class XdbfWrapper {
|
|||
|
||||
XdbfPropertyBag GetMatchCollection() const;
|
||||
|
||||
const uint8_t* ReadXLast(uint32_t& compressed_size, uint32_t& decompressed_size) const;
|
||||
const uint8_t* ReadXLast(uint32_t& compressed_size,
|
||||
uint32_t& decompressed_size) const;
|
||||
|
||||
private:
|
||||
const uint8_t* data_ = nullptr;
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include "xenia/kernel/xam/content_manager.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
|
|
|
@ -33,8 +33,7 @@
|
|||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
|
||||
DEFINE_int32(
|
||||
avpack, 8,
|
||||
DEFINE_int32(avpack, 8,
|
||||
"Video modes\n"
|
||||
" 0 = PAL-60 Component (SD)\n"
|
||||
" 1 = Unused\n"
|
||||
|
|
|
@ -431,7 +431,8 @@ dword_result_t XamGetLocaleEx_entry(dword_t max_country_id,
|
|||
static_cast<uint8_t>(max_locale_id));
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetLocaleEx, kLocale, kImplemented);
|
||||
//originally a switch table, wrote a script to extract the values for all possible cases
|
||||
// originally a switch table, wrote a script to extract the values for all
|
||||
// possible cases
|
||||
|
||||
static constexpr uint8_t XamLocaleDateFmtTable[] = {
|
||||
2, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 1, 4, 2, 3, 1, 2, 2, 3,
|
||||
|
|
|
@ -66,9 +66,9 @@ dword_result_t XamTaskSchedule_entry(lpvoid_t callback,
|
|||
// Stack must be aligned to 16kb pages
|
||||
stack_size = std::max((uint32_t)0x4000, ((stack_size + 0xFFF) & 0xFFFFF000));
|
||||
|
||||
auto thread =
|
||||
object_ref<XThread>(new XThread(kernel_state(), stack_size, 0, callback,
|
||||
message.guest_address(), 0, true, false, kernel_state()->GetSystemProcess()));
|
||||
auto thread = object_ref<XThread>(new XThread(
|
||||
kernel_state(), stack_size, 0, callback, message.guest_address(), 0, true,
|
||||
false, kernel_state()->GetSystemProcess()));
|
||||
|
||||
X_STATUS result = thread->Create();
|
||||
|
||||
|
|
|
@ -140,7 +140,8 @@ void RtlRaiseException_entry(pointer_t<X_EXCEPTION_RECORD> record) {
|
|||
// This is going to suck.
|
||||
// xe::debugging::Break();
|
||||
|
||||
//RtlRaiseException definitely wasn't a noreturn function, we can return safe-ish
|
||||
// RtlRaiseException definitely wasn't a noreturn function, we can return
|
||||
// safe-ish
|
||||
XELOGE("Guest attempted to trigger a breakpoint!");
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT2(RtlRaiseException, kDebug, kStub, kImportant);
|
||||
|
|
|
@ -723,8 +723,9 @@ dword_result_t IoCreateDevice_entry(dword_t driver_object,
|
|||
}
|
||||
DECLARE_XBOXKRNL_EXPORT1(IoCreateDevice, kFileSystem, kStub);
|
||||
|
||||
//supposed to invoke a callback on the driver object! its some sort of destructor function
|
||||
//intended to be called for all devices created from the driver
|
||||
// supposed to invoke a callback on the driver object! its some sort of
|
||||
// destructor function intended to be called for all devices created from the
|
||||
// driver
|
||||
void IoDeleteDevice_entry(dword_t device_ptr, const ppc_context_t& ctx) {
|
||||
if (device_ptr) {
|
||||
auto kernel_mem = ctx->kernel_state->memory();
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
||||
namespace xboxkrnl {
|
||||
|
||||
uint32_t xeMmAllocatePhysicalMemoryEx(uint32_t flags, uint32_t region_size,
|
||||
|
|
|
@ -434,8 +434,10 @@ pointer_result_t RtlImageNtHeader_entry(lpvoid_t module) {
|
|||
}
|
||||
DECLARE_XBOXKRNL_EXPORT1(RtlImageNtHeader, kNone, kImplemented);
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-imagedirectoryentrytodata
|
||||
dword_result_t RtlImageDirectoryEntryToData_entry(dword_t Base, dword_t MappedAsImage_,
|
||||
word_t DirectoryEntry, dword_t Size,
|
||||
dword_result_t RtlImageDirectoryEntryToData_entry(dword_t Base,
|
||||
dword_t MappedAsImage_,
|
||||
word_t DirectoryEntry,
|
||||
dword_t Size,
|
||||
const ppc_context_t& ctx) {
|
||||
bool MappedAsImage = static_cast<unsigned char>(MappedAsImage_);
|
||||
uint32_t aligned_base = Base;
|
||||
|
|
|
@ -63,7 +63,8 @@ uint32_t xeNtQueueApcThread(uint32_t thread_handle, uint32_t apc_routine,
|
|||
void xeKfLowerIrql(PPCContext* ctx, unsigned char new_irql);
|
||||
unsigned char xeKfRaiseIrql(PPCContext* ctx, unsigned char new_irql);
|
||||
|
||||
void xeKeKfReleaseSpinLock(PPCContext* ctx, X_KSPINLOCK* lock, uint32_t old_irql, bool change_irql=true);
|
||||
void xeKeKfReleaseSpinLock(PPCContext* ctx, X_KSPINLOCK* lock,
|
||||
uint32_t old_irql, bool change_irql = true);
|
||||
uint32_t xeKeKfAcquireSpinLock(PPCContext* ctx, X_KSPINLOCK* lock,
|
||||
bool change_irql = true);
|
||||
|
||||
|
|
|
@ -388,7 +388,9 @@ object_ref<XObject> XObject::GetNativeObject(KernelState* kernel_state,
|
|||
// Already initialized.
|
||||
// TODO: assert if the type of the object != as_type
|
||||
uint32_t handle = header->wait_list_blink;
|
||||
result = kernel_state->object_table()->LookupObject<XObject>(handle, true).release();
|
||||
result = kernel_state->object_table()
|
||||
->LookupObject<XObject>(handle, true)
|
||||
.release();
|
||||
} else {
|
||||
// First use, create new.
|
||||
// https://www.nirsoft.net/kernel_struct/vista/KOBJECTS.html
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
#include "xenia/xbox.h"
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
|
|
|
@ -358,6 +358,7 @@ class XThread : public XObject, public cpu::Thread {
|
|||
pending_mutant_acquires_.push_back(mutant);
|
||||
}
|
||||
void SetCurrentThread();
|
||||
|
||||
protected:
|
||||
bool AllocateStack(uint32_t size);
|
||||
void FreeStack();
|
||||
|
@ -391,7 +392,8 @@ class XThread : public XObject, public cpu::Thread {
|
|||
class XHostThread : public XThread {
|
||||
public:
|
||||
XHostThread(KernelState* kernel_state, uint32_t stack_size,
|
||||
uint32_t creation_flags, std::function<int()> host_fn, uint32_t guest_process=0);
|
||||
uint32_t creation_flags, std::function<int()> host_fn,
|
||||
uint32_t guest_process = 0);
|
||||
|
||||
virtual void Execute();
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/threading.h"
|
||||
|
||||
#include "xenia/cpu/mmio_handler.h"
|
||||
|
||||
// TODO(benvanik): move xbox.h out
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
*/
|
||||
#include <regex>
|
||||
|
||||
#include "xenia/config.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/config.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
#include "xenia/patcher/patch_db.h"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <vector>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
static const uint8_t player_one_notification_icon[] = {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
|
@ -4269,8 +4269,8 @@ static const uint8_t player_any_notification_icon[] = {
|
|||
0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82};
|
||||
static const uint32_t player_any_notification_icon_len = 10320;
|
||||
|
||||
|
||||
static const std::vector<std::pair<const uint8_t*, uint32_t>> notification_icons = {
|
||||
static const std::vector<std::pair<const uint8_t*, uint32_t>>
|
||||
notification_icons = {
|
||||
{player_one_notification_icon, player_one_notification_icon_len},
|
||||
{player_two_notification_icon, player_two_notification_icon_len},
|
||||
{player_three_notification_icon, player_three_notification_icon_len},
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
|
||||
#include <time.h>
|
||||
|
||||
#include "xenia/xbox.h"
|
||||
#include "xenia/base/string_util.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/vfs/devices/xcontent_container_device.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/stfs_container_device.h"
|
||||
#include "xenia/vfs/devices/xcontent_devices/svod_container_device.h"
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/kernel/xam/content_manager.h"
|
||||
#include "xenia/vfs/device.h"
|
||||
#include "xenia/vfs/devices/stfs_xbox.h"
|
||||
#include "xenia/kernel/xam/content_manager.h"
|
||||
|
||||
namespace xe {
|
||||
namespace vfs {
|
||||
|
@ -71,7 +71,8 @@ class XContentContainerDevice : public Device {
|
|||
};
|
||||
|
||||
virtual Result Read() = 0;
|
||||
// Load all host files. Usually STFS is only 1 file, meanwhile SVOD is usually multiple file.
|
||||
// Load all host files. Usually STFS is only 1 file, meanwhile SVOD is usually
|
||||
// multiple file.
|
||||
virtual Result LoadHostFiles(FILE* header_file) = 0;
|
||||
// Initialize any container specific fields.
|
||||
virtual void SetupContainer(){};
|
||||
|
@ -89,7 +90,9 @@ class XContentContainerDevice : public Device {
|
|||
|
||||
const std::filesystem::path& GetHostPath() const { return host_path_; }
|
||||
|
||||
const XContentContainerHeader* GetContainerHeader() const { return header_.get(); }
|
||||
const XContentContainerHeader* GetContainerHeader() const {
|
||||
return header_.get();
|
||||
}
|
||||
|
||||
std::string name_;
|
||||
std::filesystem::path host_path_;
|
||||
|
|
|
@ -312,7 +312,6 @@ struct X_EX_TITLE_TERMINATE_REGISTRATION {
|
|||
};
|
||||
static_assert_size(X_EX_TITLE_TERMINATE_REGISTRATION, 16);
|
||||
|
||||
|
||||
enum X_OBJECT_HEADER_FLAGS : uint16_t {
|
||||
OBJECT_HEADER_FLAG_NAMED_OBJECT =
|
||||
1, // if set, has X_OBJECT_HEADER_NAME_INFO prior to X_OBJECT_HEADER
|
||||
|
|
|
@ -2,120 +2,192 @@
|
|||
#
|
||||
# ===- clang-format-diff.py - ClangFormat Diff Reformatter ----*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
# ===------------------------------------------------------------------------===#
|
||||
|
||||
r"""
|
||||
ClangFormat Diff Reformatter
|
||||
============================
|
||||
|
||||
"""
|
||||
This script reads input from a unified diff and reformats all the changed
|
||||
lines. This is useful to reformat all the lines touched by a specific patch.
|
||||
Example usage for git/svn users:
|
||||
|
||||
git diff -U0 --no-color HEAD^ | clang-format-diff.py -p1 -i
|
||||
svn diff --diff-cmd=diff -x-U0 | clang-format-diff.py -i
|
||||
git diff -U0 --no-color --relative HEAD^ | {clang_format_diff} -p1 -i
|
||||
svn diff --diff-cmd=diff -x-U0 | {clang_format_diff} -i
|
||||
|
||||
It should be noted that the filename contained in the diff is used unmodified
|
||||
to determine the source file to update. Users calling this script directly
|
||||
should be careful to ensure that the path in the diff is correct relative to the
|
||||
current working directory.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
import StringIO
|
||||
import sys
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
from io import StringIO
|
||||
else:
|
||||
from io import BytesIO as StringIO
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=
|
||||
'Reformat changed lines in diff. Without -i '
|
||||
'option just output the diff that would be '
|
||||
'introduced.')
|
||||
parser.add_argument('-i', action='store_true', default=False,
|
||||
help='apply edits to files instead of displaying a diff')
|
||||
parser.add_argument('-p', metavar='NUM', default=0,
|
||||
help='strip the smallest prefix containing P slashes')
|
||||
parser.add_argument('-regex', metavar='PATTERN', default=None,
|
||||
help='custom pattern selecting file paths to reformat '
|
||||
'(case sensitive, overrides -iregex)')
|
||||
parser.add_argument('-iregex', metavar='PATTERN', default=
|
||||
r'.*\.(cpp|cc|c\+\+|cxx|c|cl|h|hpp|m|mm|inc|js|ts|proto'
|
||||
r'|protodevel|java)',
|
||||
help='custom pattern selecting file paths to reformat '
|
||||
'(case insensitive, overridden by -regex)')
|
||||
parser.add_argument('-sort-includes', action='store_true', default=False,
|
||||
help='let clang-format sort include blocks')
|
||||
parser.add_argument('-v', '--verbose', action='store_true',
|
||||
help='be more verbose, ineffective without -i')
|
||||
parser.add_argument('-style',
|
||||
help='formatting style to apply (LLVM, Google, Chromium, '
|
||||
'Mozilla, WebKit)')
|
||||
parser.add_argument('-binary', default='clang-format',
|
||||
help='location of binary to use for clang-format')
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__.format(clang_format_diff="%(prog)s"),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="apply edits to files instead of displaying a diff",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
metavar="NUM",
|
||||
default=0,
|
||||
help="strip the smallest prefix containing P slashes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-regex",
|
||||
metavar="PATTERN",
|
||||
default=None,
|
||||
help="custom pattern selecting file paths to reformat "
|
||||
"(case sensitive, overrides -iregex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-iregex",
|
||||
metavar="PATTERN",
|
||||
default=r".*\.(?:cpp|cc|c\+\+|cxx|cppm|ccm|cxxm|c\+\+m|c|cl|h|hh|hpp"
|
||||
r"|hxx|m|mm|inc|js|ts|proto|protodevel|java|cs|json|s?vh?)",
|
||||
help="custom pattern selecting file paths to reformat "
|
||||
"(case insensitive, overridden by -regex)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-sort-includes",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="let clang-format sort include blocks",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--verbose",
|
||||
action="store_true",
|
||||
help="be more verbose, ineffective without -i",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-style",
|
||||
help="formatting style to apply (LLVM, GNU, Google, Chromium, "
|
||||
"Microsoft, Mozilla, WebKit)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-fallback-style",
|
||||
help="The name of the predefined style used as a"
|
||||
"fallback in case clang-format is invoked with"
|
||||
"-style=file, but can not find the .clang-format"
|
||||
"file to use.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-binary",
|
||||
default="clang-format",
|
||||
help="location of binary to use for clang-format",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Extract changed lines for each file.
|
||||
filename = None
|
||||
lines_by_file = {}
|
||||
for line in sys.stdin:
|
||||
match = re.search('^\+\+\+\ (.*?/){%s}(\S*)' % args.p, line)
|
||||
match = re.search(r"^\+\+\+\ (.*?/){%s}(\S*)" % args.p, line)
|
||||
if match:
|
||||
filename = match.group(2)
|
||||
if filename == None:
|
||||
if filename is None:
|
||||
continue
|
||||
|
||||
if args.regex is not None:
|
||||
if not re.match('^%s$' % args.regex, filename):
|
||||
if not re.match("^%s$" % args.regex, filename):
|
||||
continue
|
||||
else:
|
||||
if not re.match('^%s$' % args.iregex, filename, re.IGNORECASE):
|
||||
if not re.match("^%s$" % args.iregex, filename, re.IGNORECASE):
|
||||
continue
|
||||
|
||||
match = re.search('^@@.*\+(\d+)(,(\d+))?', line)
|
||||
match = re.search(r"^@@.*\+(\d+)(?:,(\d+))?", line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(3):
|
||||
line_count = int(match.group(3))
|
||||
if match.group(2):
|
||||
line_count = int(match.group(2))
|
||||
# The input is something like
|
||||
#
|
||||
# @@ -1, +0,0 @@
|
||||
#
|
||||
# which means no lines were added.
|
||||
if line_count == 0:
|
||||
continue
|
||||
end_line = start_line + line_count - 1;
|
||||
# Also format lines range if line_count is 0 in case of deleting
|
||||
# surrounding statements.
|
||||
end_line = start_line
|
||||
if line_count != 0:
|
||||
end_line += line_count - 1
|
||||
lines_by_file.setdefault(filename, []).extend(
|
||||
['-lines', str(start_line) + ':' + str(end_line)])
|
||||
["-lines", str(start_line) + ":" + str(end_line)]
|
||||
)
|
||||
|
||||
# Reformat files containing changes in place.
|
||||
for filename, lines in lines_by_file.iteritems():
|
||||
for filename, lines in lines_by_file.items():
|
||||
if args.i and args.verbose:
|
||||
print 'Formatting', filename
|
||||
print("Formatting {}".format(filename))
|
||||
command = [args.binary, filename]
|
||||
if args.i:
|
||||
command.append('-i')
|
||||
command.append("-i")
|
||||
if args.sort_includes:
|
||||
command.append('-sort-includes')
|
||||
command.append("-sort-includes")
|
||||
command.extend(lines)
|
||||
if args.style:
|
||||
command.extend(['-style', args.style])
|
||||
p = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||
stderr=None, stdin=subprocess.PIPE)
|
||||
command.extend(["-style", args.style])
|
||||
if args.fallback_style:
|
||||
command.extend(["-fallback-style", args.fallback_style])
|
||||
|
||||
try:
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stdin=subprocess.PIPE,
|
||||
universal_newlines=True,
|
||||
)
|
||||
except OSError as e:
|
||||
# Give the user more context when clang-format isn't
|
||||
# found/isn't executable, etc.
|
||||
raise RuntimeError(
|
||||
'Failed to run "%s" - %s"' % (" ".join(command), e.strerror)
|
||||
)
|
||||
|
||||
stdout, stderr = p.communicate()
|
||||
if p.returncode != 0:
|
||||
sys.exit(p.returncode);
|
||||
sys.exit(p.returncode)
|
||||
|
||||
if not args.i:
|
||||
with open(filename) as f:
|
||||
code = f.readlines()
|
||||
formatted_code = StringIO.StringIO(stdout).readlines()
|
||||
diff = difflib.unified_diff(code, formatted_code,
|
||||
filename, filename,
|
||||
'(before formatting)', '(after formatting)')
|
||||
diff_string = string.join(diff, '')
|
||||
formatted_code = StringIO(stdout).readlines()
|
||||
diff = difflib.unified_diff(
|
||||
code,
|
||||
formatted_code,
|
||||
filename,
|
||||
filename,
|
||||
"(before formatting)",
|
||||
"(after formatting)",
|
||||
)
|
||||
diff_string = "".join(diff)
|
||||
if len(diff_string) > 0:
|
||||
sys.stdout.write(diff_string)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
@ -12,47 +12,62 @@
|
|||
# It operates on the current, potentially unsaved buffer and does not create
|
||||
# or save any files. To revert a formatting, just undo.
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import sublime
|
||||
import sublime_plugin
|
||||
import subprocess
|
||||
|
||||
# Change this to the full path if clang-format is not on the path.
|
||||
binary = 'clang-format'
|
||||
binary = "clang-format"
|
||||
|
||||
# Change this to format according to other formatting styles. See the output of
|
||||
# 'clang-format --help' for a list of supported styles. The default looks for
|
||||
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
||||
# used.
|
||||
style = 'file'
|
||||
style = None
|
||||
|
||||
|
||||
class ClangFormatCommand(sublime_plugin.TextCommand):
|
||||
def run(self, edit):
|
||||
encoding = self.view.encoding()
|
||||
if encoding == 'Undefined':
|
||||
encoding = 'utf-8'
|
||||
if encoding == "Undefined":
|
||||
encoding = "utf-8"
|
||||
regions = []
|
||||
command = [binary, '-style', style]
|
||||
command = [binary]
|
||||
if style:
|
||||
command.extend(["-style", style])
|
||||
for region in self.view.sel():
|
||||
regions.append(region)
|
||||
region_offset = min(region.a, region.b)
|
||||
region_length = abs(region.b - region.a)
|
||||
command.extend(['-offset', str(region_offset),
|
||||
'-length', str(region_length),
|
||||
'-assume-filename', str(self.view.file_name())])
|
||||
command.extend(
|
||||
[
|
||||
"-offset",
|
||||
str(region_offset),
|
||||
"-length",
|
||||
str(region_length),
|
||||
"-assume-filename",
|
||||
str(self.view.file_name()),
|
||||
]
|
||||
)
|
||||
old_viewport_position = self.view.viewport_position()
|
||||
buf = self.view.substr(sublime.Region(0, self.view.size()))
|
||||
p = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
)
|
||||
output, error = p.communicate(buf.encode(encoding))
|
||||
if error:
|
||||
print(error)
|
||||
self.view.replace(
|
||||
edit, sublime.Region(0, self.view.size()),
|
||||
output.decode(encoding))
|
||||
edit, sublime.Region(0, self.view.size()), output.decode(encoding)
|
||||
)
|
||||
self.view.sel().clear()
|
||||
for region in regions:
|
||||
self.view.sel().add(region)
|
||||
# FIXME: Without the 10ms delay, the viewport sometimes jumps.
|
||||
sublime.set_timeout(lambda: self.view.set_viewport_position(
|
||||
old_viewport_position, False), 10)
|
||||
sublime.set_timeout(
|
||||
lambda: self.view.set_viewport_position(old_viewport_position, False), 10
|
||||
)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
;;; clang-format.el --- Format code using clang-format -*- lexical-binding: t; -*-
|
||||
|
||||
;; Version: 0.1.0
|
||||
;; Keywords: tools, c
|
||||
;; Package-Requires: ((cl-lib "0.3"))
|
||||
;; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
|
@ -45,17 +47,29 @@ A string containing the name or the full path of the executable."
|
|||
:type '(file :must-match t)
|
||||
:risky t)
|
||||
|
||||
(defcustom clang-format-style "file"
|
||||
(defcustom clang-format-style nil
|
||||
"Style argument to pass to clang-format.
|
||||
|
||||
By default clang-format will load the style configuration from
|
||||
a file named .clang-format located in one of the parent directories
|
||||
of the buffer."
|
||||
:group 'clang-format
|
||||
:type 'string
|
||||
:type '(choice (string) (const nil))
|
||||
:safe #'stringp)
|
||||
(make-variable-buffer-local 'clang-format-style)
|
||||
|
||||
(defcustom clang-format-fallback-style "none"
|
||||
"Fallback style to pass to clang-format.
|
||||
|
||||
This style will be used if clang-format-style is set to \"file\"
|
||||
and no .clang-format is found in the directory of the buffer or
|
||||
one of parent directories. Set to \"none\" to disable formatting
|
||||
in such buffers."
|
||||
:group 'clang-format
|
||||
:type 'string
|
||||
:safe #'stringp)
|
||||
(make-variable-buffer-local 'clang-format-fallback-style)
|
||||
|
||||
(defun clang-format--extract (xml-node)
|
||||
"Extract replacements and cursor information from XML-NODE."
|
||||
(unless (and (listp xml-node) (eq (xml-node-name xml-node) 'replacements))
|
||||
|
@ -69,7 +83,7 @@ of the buffer."
|
|||
(let* ((children (xml-node-children node))
|
||||
(text (car children)))
|
||||
(cl-case (xml-node-name node)
|
||||
('replacement
|
||||
(replacement
|
||||
(let* ((offset (xml-get-attribute-or-nil node 'offset))
|
||||
(length (xml-get-attribute-or-nil node 'length)))
|
||||
(when (or (null offset) (null length))
|
||||
|
@ -80,7 +94,7 @@ of the buffer."
|
|||
(setq offset (string-to-number offset))
|
||||
(setq length (string-to-number length))
|
||||
(push (list offset length text) replacements)))
|
||||
('cursor
|
||||
(cursor
|
||||
(setq cursor (string-to-number text)))))))
|
||||
|
||||
;; Sort by decreasing offset, length.
|
||||
|
@ -119,10 +133,12 @@ is a zero-based file offset, assuming ‘utf-8-unix’ coding."
|
|||
(byte-to-position (1+ byte)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-format-region (start end &optional style)
|
||||
(defun clang-format-region (start end &optional style assume-file-name)
|
||||
"Use clang-format to format the code between START and END according to STYLE.
|
||||
If called interactively uses the region or the current statement if there
|
||||
is no active region. If no style is given uses `clang-format-style'."
|
||||
If called interactively uses the region or the current statement if there is no
|
||||
no active region. If no STYLE is given uses `clang-format-style'. Use
|
||||
ASSUME-FILE-NAME to locate a style config file, if no ASSUME-FILE-NAME is given
|
||||
uses the function `buffer-file-name'."
|
||||
(interactive
|
||||
(if (use-region-p)
|
||||
(list (region-beginning) (region-end))
|
||||
|
@ -131,6 +147,9 @@ is no active region. If no style is given uses `clang-format-style'."
|
|||
(unless style
|
||||
(setq style clang-format-style))
|
||||
|
||||
(unless assume-file-name
|
||||
(setq assume-file-name (buffer-file-name (buffer-base-buffer))))
|
||||
|
||||
(let ((file-start (clang-format--bufferpos-to-filepos start 'approximate
|
||||
'utf-8-unix))
|
||||
(file-end (clang-format--bufferpos-to-filepos end 'approximate
|
||||
|
@ -144,16 +163,22 @@ is no active region. If no style is given uses `clang-format-style'."
|
|||
;; always use ‘utf-8-unix’ and ignore the buffer coding system.
|
||||
(default-process-coding-system '(utf-8-unix . utf-8-unix)))
|
||||
(unwind-protect
|
||||
(let ((status (call-process-region
|
||||
(let ((status (apply #'call-process-region
|
||||
nil nil clang-format-executable
|
||||
nil `(,temp-buffer ,temp-file) nil
|
||||
|
||||
"-output-replacements-xml"
|
||||
"-assume-filename" (or (buffer-file-name) "")
|
||||
"-style" style
|
||||
"-offset" (number-to-string file-start)
|
||||
"-length" (number-to-string (- file-end file-start))
|
||||
"-cursor" (number-to-string cursor)))
|
||||
`("-output-replacements-xml"
|
||||
;; Guard against a nil assume-file-name.
|
||||
;; If the clang-format option -assume-filename
|
||||
;; is given a blank string it will crash as per
|
||||
;; the following bug report
|
||||
;; https://bugs.llvm.org/show_bug.cgi?id=34667
|
||||
,@(and assume-file-name
|
||||
(list "-assume-filename" assume-file-name))
|
||||
,@(and style (list "-style" style))
|
||||
"-fallback-style" ,clang-format-fallback-style
|
||||
"-offset" ,(number-to-string file-start)
|
||||
"-length" ,(number-to-string (- file-end file-start))
|
||||
"-cursor" ,(number-to-string cursor))))
|
||||
(stderr (with-temp-buffer
|
||||
(unless (zerop (cadr (insert-file-contents temp-file)))
|
||||
(insert ": "))
|
||||
|
@ -181,10 +206,13 @@ is no active region. If no style is given uses `clang-format-style'."
|
|||
(when (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun clang-format-buffer (&optional style)
|
||||
"Use clang-format to format the current buffer according to STYLE."
|
||||
(defun clang-format-buffer (&optional style assume-file-name)
|
||||
"Use clang-format to format the current buffer according to STYLE.
|
||||
If no STYLE is given uses `clang-format-style'. Use ASSUME-FILE-NAME
|
||||
to locate a style config file. If no ASSUME-FILE-NAME is given uses
|
||||
the function `buffer-file-name'."
|
||||
(interactive)
|
||||
(clang-format-region (point-min) (point-max) style))
|
||||
(clang-format-region (point-min) (point-max) style assume-file-name))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'clang-format 'clang-format-region)
|
||||
|
|
|
@ -2,11 +2,19 @@
|
|||
# - Change 'binary' if clang-format is not on the path (see below).
|
||||
# - Add to your .vimrc:
|
||||
#
|
||||
# if has('python')
|
||||
# map <C-I> :pyf <path-to-this-file>/clang-format.py<cr>
|
||||
# imap <C-I> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
|
||||
# elseif has('python3')
|
||||
# map <C-I> :py3f <path-to-this-file>/clang-format.py<cr>
|
||||
# imap <C-I> <c-o>:py3f <path-to-this-file>/clang-format.py<cr>
|
||||
# endif
|
||||
#
|
||||
# The first line enables clang-format for NORMAL and VISUAL mode, the second
|
||||
# line adds support for INSERT mode. Change "C-I" to another binding if you
|
||||
# The if-elseif-endif conditional should pick either the python3 or python2
|
||||
# integration depending on your vim setup.
|
||||
#
|
||||
# The first mapping enables clang-format for NORMAL and VISUAL mode, the second
|
||||
# mapping adds support for INSERT mode. Change "C-I" to another binding if you
|
||||
# need clang-format on a different key (C-I stands for Ctrl+i).
|
||||
#
|
||||
# With this integration you can press the bound key and clang-format will
|
||||
|
@ -20,15 +28,20 @@
|
|||
# like:
|
||||
# :function FormatFile()
|
||||
# : let l:lines="all"
|
||||
# : if has('python')
|
||||
# : pyf <path-to-this-file>/clang-format.py
|
||||
# : elseif has('python3')
|
||||
# : py3f <path-to-this-file>/clang-format.py
|
||||
# : endif
|
||||
# :endfunction
|
||||
#
|
||||
# It operates on the current, potentially unsaved buffer and does not create
|
||||
# or save any files. To revert a formatting, just undo.
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import difflib
|
||||
import json
|
||||
import os.path
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
|
@ -36,72 +49,90 @@ import vim
|
|||
|
||||
# set g:clang_format_path to the path to clang-format if it is not on the path
|
||||
# Change this to the full path if clang-format is not on the path.
|
||||
binary = 'clang-format'
|
||||
binary = "clang-format"
|
||||
if vim.eval('exists("g:clang_format_path")') == "1":
|
||||
binary = vim.eval('g:clang_format_path')
|
||||
binary = vim.eval("g:clang_format_path")
|
||||
|
||||
# Change this to format according to other formatting styles. See the output of
|
||||
# 'clang-format --help' for a list of supported styles. The default looks for
|
||||
# a '.clang-format' or '_clang-format' file to indicate the style that should be
|
||||
# used.
|
||||
style = 'file'
|
||||
style = None
|
||||
fallback_style = None
|
||||
if vim.eval('exists("g:clang_format_fallback_style")') == "1":
|
||||
fallback_style = vim.eval('g:clang_format_fallback_style')
|
||||
fallback_style = vim.eval("g:clang_format_fallback_style")
|
||||
|
||||
|
||||
def get_buffer(encoding):
|
||||
if platform.python_version_tuple()[0] == '3':
|
||||
if platform.python_version_tuple()[0] == "3":
|
||||
return vim.current.buffer
|
||||
return [line.decode(encoding) for line in vim.current.buffer]
|
||||
|
||||
|
||||
def main():
|
||||
# Get the current text.
|
||||
encoding = vim.eval("&encoding")
|
||||
buf = get_buffer(encoding)
|
||||
text = '\n'.join(buf)
|
||||
# Join the buffer into a single string with a terminating newline
|
||||
text = ("\n".join(buf) + "\n").encode(encoding)
|
||||
|
||||
# Determine range to format.
|
||||
if vim.eval('exists("l:lines")') == '1':
|
||||
lines = vim.eval('l:lines')
|
||||
elif vim.eval('exists("l:formatdiff")') == '1':
|
||||
with open(vim.current.buffer.name, 'r') as f:
|
||||
ondisk = f.read().splitlines();
|
||||
if vim.eval('exists("l:lines")') == "1":
|
||||
lines = ["-lines", vim.eval("l:lines")]
|
||||
elif vim.eval('exists("l:formatdiff")') == "1" and os.path.exists(
|
||||
vim.current.buffer.name
|
||||
):
|
||||
with open(vim.current.buffer.name, "r") as f:
|
||||
ondisk = f.read().splitlines()
|
||||
sequence = difflib.SequenceMatcher(None, ondisk, vim.current.buffer)
|
||||
lines = []
|
||||
for op in reversed(sequence.get_opcodes()):
|
||||
if op[0] not in ['equal', 'delete']:
|
||||
lines += ['-lines', '%s:%s' % (op[3] + 1, op[4])]
|
||||
if op[0] not in ["equal", "delete"]:
|
||||
lines += ["-lines", "%s:%s" % (op[3] + 1, op[4])]
|
||||
if lines == []:
|
||||
return
|
||||
else:
|
||||
lines = ['-lines', '%s:%s' % (vim.current.range.start + 1,
|
||||
vim.current.range.end + 1)]
|
||||
lines = [
|
||||
"-lines",
|
||||
"%s:%s" % (vim.current.range.start + 1, vim.current.range.end + 1),
|
||||
]
|
||||
|
||||
# Determine the cursor position.
|
||||
cursor = int(vim.eval('line2byte(line("."))+col(".")')) - 2
|
||||
if cursor < 0:
|
||||
print('Couldn\'t determine cursor position. Is your file empty?')
|
||||
# Convert cursor (line, col) to bytes.
|
||||
# Don't use line2byte: https://github.com/vim/vim/issues/5930
|
||||
_, cursor_line, cursor_col, _ = vim.eval('getpos(".")') # 1-based
|
||||
cursor_byte = 0
|
||||
for line in text.split(b"\n")[: int(cursor_line) - 1]:
|
||||
cursor_byte += len(line) + 1
|
||||
cursor_byte += int(cursor_col) - 1
|
||||
if cursor_byte < 0:
|
||||
print("Couldn't determine cursor position. Is your file empty?")
|
||||
return
|
||||
|
||||
# Avoid flashing an ugly, ugly cmd prompt on Windows when invoking clang-format.
|
||||
startupinfo = None
|
||||
if sys.platform.startswith('win32'):
|
||||
if sys.platform.startswith("win32"):
|
||||
startupinfo = subprocess.STARTUPINFO()
|
||||
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
||||
startupinfo.wShowWindow = subprocess.SW_HIDE
|
||||
|
||||
# Call formatter.
|
||||
command = [binary, '-style', style, '-cursor', str(cursor)]
|
||||
if lines != 'all':
|
||||
command = [binary, "-cursor", str(cursor_byte)]
|
||||
if lines != ["-lines", "all"]:
|
||||
command += lines
|
||||
if style:
|
||||
command.extend(["-style", style])
|
||||
if fallback_style:
|
||||
command.extend(['-fallback-style', fallback_style])
|
||||
command.extend(["-fallback-style", fallback_style])
|
||||
if vim.current.buffer.name:
|
||||
command.extend(['-assume-filename', vim.current.buffer.name])
|
||||
p = subprocess.Popen(command,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE, startupinfo=startupinfo)
|
||||
stdout, stderr = p.communicate(input=text.encode(encoding))
|
||||
command.extend(["-assume-filename", vim.current.buffer.name])
|
||||
p = subprocess.Popen(
|
||||
command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
stdin=subprocess.PIPE,
|
||||
startupinfo=startupinfo,
|
||||
)
|
||||
stdout, stderr = p.communicate(input=text)
|
||||
|
||||
# If successful, replace buffer contents.
|
||||
if stderr:
|
||||
|
@ -109,19 +140,29 @@ def main():
|
|||
|
||||
if not stdout:
|
||||
print(
|
||||
'No output from clang-format (crashed?).\n'
|
||||
'Please report to bugs.llvm.org.'
|
||||
"No output from clang-format (crashed?).\n"
|
||||
"Please report to bugs.llvm.org."
|
||||
)
|
||||
else:
|
||||
lines = stdout.decode(encoding).split('\n')
|
||||
output = json.loads(lines[0])
|
||||
lines = lines[1:]
|
||||
header, content = stdout.split(b"\n", 1)
|
||||
header = json.loads(header.decode("utf-8"))
|
||||
# Strip off the trailing newline (added above).
|
||||
# This maintains trailing empty lines present in the buffer if
|
||||
# the -lines specification requests them to remain unchanged.
|
||||
lines = content.decode(encoding).split("\n")[:-1]
|
||||
sequence = difflib.SequenceMatcher(None, buf, lines)
|
||||
for op in reversed(sequence.get_opcodes()):
|
||||
if op[0] is not 'equal':
|
||||
if op[0] != "equal":
|
||||
vim.current.buffer[op[1] : op[2]] = lines[op[3] : op[4]]
|
||||
if output.get('IncompleteFormat'):
|
||||
print('clang-format: incomplete (syntax errors)')
|
||||
vim.command('goto %d' % (output['Cursor'] + 1))
|
||||
if header.get("IncompleteFormat"):
|
||||
print("clang-format: incomplete (syntax errors)")
|
||||
# Convert cursor bytes to (line, col)
|
||||
# Don't use goto: https://github.com/vim/vim/issues/5930
|
||||
cursor_byte = int(header["Cursor"])
|
||||
prefix = content[0:cursor_byte]
|
||||
cursor_line = 1 + prefix.count(b"\n")
|
||||
cursor_column = 1 + len(prefix.rsplit(b"\n", 1)[-1])
|
||||
vim.command("call cursor(%d, %d)" % (cursor_line, cursor_column))
|
||||
|
||||
|
||||
main()
|
||||
|
|
|
@ -2,10 +2,9 @@
|
|||
#
|
||||
#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
# See https://llvm.org/LICENSE.txt for license information.
|
||||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
|
@ -23,7 +22,7 @@ git clang-format -h
|
|||
Requires Python 2.7 or Python 3
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import argparse
|
||||
import collections
|
||||
import contextlib
|
||||
|
@ -33,12 +32,23 @@ import re
|
|||
import subprocess
|
||||
import sys
|
||||
|
||||
usage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]'
|
||||
usage = ('git clang-format [OPTIONS] [<commit>] [<commit>|--staged] '
|
||||
'[--] [<file>...]')
|
||||
|
||||
desc = '''
|
||||
If zero or one commits are given, run clang-format on all lines that differ
|
||||
between the working directory and <commit>, which defaults to HEAD. Changes are
|
||||
only applied to the working directory.
|
||||
only applied to the working directory, or in the stage/index.
|
||||
|
||||
Examples:
|
||||
To format staged changes, i.e everything that's been `git add`ed:
|
||||
git clang-format
|
||||
|
||||
To also format everything touched in the most recent commit:
|
||||
git clang-format HEAD~1
|
||||
|
||||
If you're on a branch off main, to format everything touched on your branch:
|
||||
git clang-format main
|
||||
|
||||
If two commits are given (requires --diff), run clang-format on all lines in the
|
||||
second <commit> that differ from the first <commit>.
|
||||
|
@ -46,7 +56,7 @@ second <commit> that differ from the first <commit>.
|
|||
The following git-config settings set the default of the corresponding option:
|
||||
clangFormat.binary
|
||||
clangFormat.commit
|
||||
clangFormat.extension
|
||||
clangFormat.extensions
|
||||
clangFormat.style
|
||||
'''
|
||||
|
||||
|
@ -78,12 +88,17 @@ def main():
|
|||
'c', 'h', # C
|
||||
'm', # ObjC
|
||||
'mm', # ObjC++
|
||||
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hpp', # C++
|
||||
'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', 'inc', # C++
|
||||
'ccm', 'cppm', 'cxxm', 'c++m', # C++ Modules
|
||||
'cu', 'cuh', # CUDA
|
||||
# Other languages that clang-format supports
|
||||
'proto', 'protodevel', # Protocol Buffers
|
||||
'java', # Java
|
||||
'js', # JavaScript
|
||||
'ts', # TypeScript
|
||||
'cs', # C Sharp
|
||||
'json', # Json
|
||||
'sv', 'svh', 'v', 'vh', # Verilog
|
||||
])
|
||||
|
||||
p = argparse.ArgumentParser(
|
||||
|
@ -97,6 +112,8 @@ def main():
|
|||
help='default commit to use if none is specified'),
|
||||
p.add_argument('--diff', action='store_true',
|
||||
help='print a diff instead of applying the changes')
|
||||
p.add_argument('--diffstat', action='store_true',
|
||||
help='print a diffstat instead of applying the changes')
|
||||
p.add_argument('--extensions',
|
||||
default=config.get('clangformat.extensions',
|
||||
default_extensions),
|
||||
|
@ -108,11 +125,17 @@ def main():
|
|||
help='select hunks interactively')
|
||||
p.add_argument('-q', '--quiet', action='count', default=0,
|
||||
help='print less information')
|
||||
p.add_argument('--staged', '--cached', action='store_true',
|
||||
help='format lines in the stage instead of the working dir')
|
||||
p.add_argument('--style',
|
||||
default=config.get('clangformat.style', None),
|
||||
help='passed to clang-format'),
|
||||
p.add_argument('-v', '--verbose', action='count', default=0,
|
||||
help='print extra information')
|
||||
p.add_argument('--diff_from_common_commit', action='store_true',
|
||||
help=('diff from the last common commit for commits in '
|
||||
'separate branches rather than the exact point of the '
|
||||
'commits'))
|
||||
# We gather all the remaining positional arguments into 'args' since we need
|
||||
# to use some heuristics to determine whether or not <commit> was present.
|
||||
# However, to print pretty messages, we make use of metavar and help.
|
||||
|
@ -126,52 +149,74 @@ def main():
|
|||
del opts.quiet
|
||||
|
||||
commits, files = interpret_args(opts.args, dash_dash, opts.commit)
|
||||
if len(commits) > 1:
|
||||
if not opts.diff:
|
||||
die('--diff is required when two commits are given')
|
||||
else:
|
||||
if len(commits) > 2:
|
||||
die('at most two commits allowed; %d given' % len(commits))
|
||||
changed_lines = compute_diff_and_extract_lines(commits, files)
|
||||
if len(commits) == 2:
|
||||
if opts.staged:
|
||||
die('--staged is not allowed when two commits are given')
|
||||
if not opts.diff:
|
||||
die('--diff is required when two commits are given')
|
||||
elif opts.diff_from_common_commit:
|
||||
die('--diff_from_common_commit is only allowed when two commits are given')
|
||||
|
||||
if os.path.dirname(opts.binary):
|
||||
opts.binary = os.path.abspath(opts.binary)
|
||||
|
||||
changed_lines = compute_diff_and_extract_lines(commits,
|
||||
files,
|
||||
opts.staged,
|
||||
opts.diff_from_common_commit)
|
||||
if opts.verbose >= 1:
|
||||
ignored_files = set(changed_lines)
|
||||
filter_by_extension(changed_lines, opts.extensions.lower().split(','))
|
||||
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||
# those files.
|
||||
cd_to_toplevel()
|
||||
filter_symlinks(changed_lines)
|
||||
if opts.verbose >= 1:
|
||||
ignored_files.difference_update(changed_lines)
|
||||
if ignored_files:
|
||||
print('Ignoring changes in the following files (wrong extension):')
|
||||
print(
|
||||
'Ignoring changes in the following files (wrong extension or symlink):')
|
||||
for filename in ignored_files:
|
||||
print(' %s' % filename)
|
||||
if changed_lines:
|
||||
print('Running clang-format on the following files:')
|
||||
for filename in changed_lines:
|
||||
print(' %s' % filename)
|
||||
|
||||
if not changed_lines:
|
||||
if opts.verbose >= 0:
|
||||
print('no modified files to format')
|
||||
return
|
||||
# The computed diff outputs absolute paths, so we must cd before accessing
|
||||
# those files.
|
||||
cd_to_toplevel()
|
||||
return 0
|
||||
|
||||
if len(commits) > 1:
|
||||
old_tree = commits[1]
|
||||
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
||||
revision=commits[1],
|
||||
binary=opts.binary,
|
||||
style=opts.style)
|
||||
revision = old_tree
|
||||
elif opts.staged:
|
||||
old_tree = create_tree_from_index(changed_lines)
|
||||
revision = ''
|
||||
else:
|
||||
old_tree = create_tree_from_workdir(changed_lines)
|
||||
revision = None
|
||||
new_tree = run_clang_format_and_save_to_tree(changed_lines,
|
||||
revision,
|
||||
binary=opts.binary,
|
||||
style=opts.style)
|
||||
if opts.verbose >= 1:
|
||||
print('old tree: %s' % old_tree)
|
||||
print('new tree: %s' % new_tree)
|
||||
|
||||
if old_tree == new_tree:
|
||||
if opts.verbose >= 0:
|
||||
print('clang-format did not modify any files')
|
||||
elif opts.diff:
|
||||
print_diff(old_tree, new_tree)
|
||||
else:
|
||||
return 0
|
||||
|
||||
if opts.diff:
|
||||
return print_diff(old_tree, new_tree)
|
||||
if opts.diffstat:
|
||||
return print_diffstat(old_tree, new_tree)
|
||||
|
||||
changed_files = apply_changes(old_tree, new_tree, force=opts.force,
|
||||
patch_mode=opts.patch)
|
||||
if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
|
||||
|
@ -179,6 +224,8 @@ def main():
|
|||
for filename in changed_files:
|
||||
print(' %s' % filename)
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
def load_git_config(non_string_options=None):
|
||||
"""Return the git configuration as a dictionary.
|
||||
|
@ -191,7 +238,12 @@ def load_git_config(non_string_options=None):
|
|||
out = {}
|
||||
for entry in run('git', 'config', '--list', '--null').split('\0'):
|
||||
if entry:
|
||||
if '\n' in entry:
|
||||
name, value = entry.split('\n', 1)
|
||||
else:
|
||||
# A setting with no '=' ('\n' with --null) is implicitly 'true'
|
||||
name = entry
|
||||
value = 'true'
|
||||
if name in non_string_options:
|
||||
value = run('git', 'config', non_string_options[name], name)
|
||||
out[name] = value
|
||||
|
@ -261,9 +313,9 @@ def get_object_type(value):
|
|||
return convert_string(stdout.strip())
|
||||
|
||||
|
||||
def compute_diff_and_extract_lines(commits, files):
|
||||
def compute_diff_and_extract_lines(commits, files, staged, diff_common_commit):
|
||||
"""Calls compute_diff() followed by extract_lines()."""
|
||||
diff_process = compute_diff(commits, files)
|
||||
diff_process = compute_diff(commits, files, staged, diff_common_commit)
|
||||
changed_lines = extract_lines(diff_process.stdout)
|
||||
diff_process.stdout.close()
|
||||
diff_process.wait()
|
||||
|
@ -273,17 +325,24 @@ def compute_diff_and_extract_lines(commits, files):
|
|||
return changed_lines
|
||||
|
||||
|
||||
def compute_diff(commits, files):
|
||||
def compute_diff(commits, files, staged, diff_common_commit):
|
||||
"""Return a subprocess object producing the diff from `commits`.
|
||||
|
||||
The return value's `stdin` file object will produce a patch with the
|
||||
differences between the working directory and the first commit if a single
|
||||
one was specified, or the difference between both specified commits, filtered
|
||||
on `files` (if non-empty). Zero context lines are used in the patch."""
|
||||
differences between the working directory (or stage if --staged is used) and
|
||||
the first commit if a single one was specified, or the difference between
|
||||
both specified commits, filtered on `files` (if non-empty).
|
||||
Zero context lines are used in the patch."""
|
||||
git_tool = 'diff-index'
|
||||
if len(commits) > 1:
|
||||
extra_args = []
|
||||
if len(commits) == 2:
|
||||
git_tool = 'diff-tree'
|
||||
cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--']
|
||||
if diff_common_commit:
|
||||
commits = [f'{commits[0]}...{commits[1]}']
|
||||
elif staged:
|
||||
extra_args += ['--cached']
|
||||
|
||||
cmd = ['git', git_tool, '-p', '-U0'] + extra_args + commits + ['--']
|
||||
cmd.extend(files)
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
p.stdin.close()
|
||||
|
@ -304,14 +363,17 @@ def extract_lines(patch_file):
|
|||
line = convert_string(line)
|
||||
match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
|
||||
if match:
|
||||
filename = match.group(1).rstrip('\r\n')
|
||||
filename = match.group(1).rstrip('\r\n\t')
|
||||
match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
|
||||
if match:
|
||||
start_line = int(match.group(1))
|
||||
line_count = 1
|
||||
if match.group(3):
|
||||
line_count = int(match.group(3))
|
||||
if line_count > 0:
|
||||
if line_count == 0:
|
||||
line_count = 1
|
||||
if start_line == 0:
|
||||
continue
|
||||
matches.setdefault(filename, []).append(Range(start_line, line_count))
|
||||
return matches
|
||||
|
||||
|
@ -330,6 +392,13 @@ def filter_by_extension(dictionary, allowed_extensions):
|
|||
del dictionary[filename]
|
||||
|
||||
|
||||
def filter_symlinks(dictionary):
|
||||
"""Delete every key in `dictionary` that is a symlink."""
|
||||
for filename in list(dictionary.keys()):
|
||||
if os.path.islink(filename):
|
||||
del dictionary[filename]
|
||||
|
||||
|
||||
def cd_to_toplevel():
|
||||
"""Change to the top level of the git repository."""
|
||||
toplevel = run('git', 'rev-parse', '--show-toplevel')
|
||||
|
@ -343,11 +412,29 @@ def create_tree_from_workdir(filenames):
|
|||
return create_tree(filenames, '--stdin')
|
||||
|
||||
|
||||
def create_tree_from_index(filenames):
|
||||
# Copy the environment, because the files have to be read from the original
|
||||
# index.
|
||||
env = os.environ.copy()
|
||||
def index_contents_generator():
|
||||
for filename in filenames:
|
||||
git_ls_files_cmd = ['git', 'ls-files', '--stage', '-z', '--', filename]
|
||||
git_ls_files = subprocess.Popen(git_ls_files_cmd, env=env,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
stdout = git_ls_files.communicate()[0]
|
||||
yield convert_string(stdout.split(b'\0')[0])
|
||||
return create_tree(index_contents_generator(), '--index-info')
|
||||
|
||||
|
||||
def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
||||
binary='clang-format', style=None):
|
||||
"""Run clang-format on each file and save the result to a git tree.
|
||||
|
||||
Returns the object ID (SHA-1) of the created tree."""
|
||||
# Copy the environment when formatting the files in the index, because the
|
||||
# files have to be read from the original index.
|
||||
env = os.environ.copy() if revision == '' else None
|
||||
def iteritems(container):
|
||||
try:
|
||||
return container.iteritems() # Python 2
|
||||
|
@ -355,11 +442,15 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
|||
return container.items() # Python 3
|
||||
def index_info_generator():
|
||||
for filename, line_ranges in iteritems(changed_lines):
|
||||
if revision:
|
||||
if revision is not None:
|
||||
if len(revision) > 0:
|
||||
git_metadata_cmd = ['git', 'ls-tree',
|
||||
'%s:%s' % (revision, os.path.dirname(filename)),
|
||||
os.path.basename(filename)]
|
||||
git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE,
|
||||
else:
|
||||
git_metadata_cmd = ['git', 'ls-files', '--stage', '--', filename]
|
||||
git_metadata = subprocess.Popen(git_metadata_cmd, env=env,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
stdout = git_metadata.communicate()[0]
|
||||
mode = oct(int(stdout.split()[0], 8))
|
||||
|
@ -371,7 +462,8 @@ def run_clang_format_and_save_to_tree(changed_lines, revision=None,
|
|||
blob_id = clang_format_to_blob(filename, line_ranges,
|
||||
revision=revision,
|
||||
binary=binary,
|
||||
style=style)
|
||||
style=style,
|
||||
env=env)
|
||||
yield '%s %s\t%s' % (mode, blob_id, filename)
|
||||
return create_tree(index_info_generator(), '--index-info')
|
||||
|
||||
|
@ -397,11 +489,12 @@ def create_tree(input_lines, mode):
|
|||
|
||||
|
||||
def clang_format_to_blob(filename, line_ranges, revision=None,
|
||||
binary='clang-format', style=None):
|
||||
binary='clang-format', style=None, env=None):
|
||||
"""Run clang-format on the given file and save the result to a git blob.
|
||||
|
||||
Runs on the file in `revision` if not None, or on the file in the working
|
||||
directory if `revision` is None.
|
||||
directory if `revision` is None. Revision can be set to an empty string to run
|
||||
clang-format on the file in the index.
|
||||
|
||||
Returns the object ID (SHA-1) of the created blob."""
|
||||
clang_format_cmd = [binary]
|
||||
|
@ -410,10 +503,10 @@ def clang_format_to_blob(filename, line_ranges, revision=None,
|
|||
clang_format_cmd.extend([
|
||||
'-lines=%s:%s' % (start_line, start_line+line_count-1)
|
||||
for start_line, line_count in line_ranges])
|
||||
if revision:
|
||||
if revision is not None:
|
||||
clang_format_cmd.extend(['-assume-filename='+filename])
|
||||
git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
|
||||
git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE,
|
||||
git_show = subprocess.Popen(git_show_cmd, env=env, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
git_show.stdin.close()
|
||||
clang_format_stdin = git_show.stdout
|
||||
|
@ -485,9 +578,20 @@ def print_diff(old_tree, new_tree):
|
|||
# We also only print modified files since `new_tree` only contains the files
|
||||
# that were modified, so unmodified files would show as deleted without the
|
||||
# filter.
|
||||
subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree,
|
||||
'--'])
|
||||
return subprocess.run(['git', 'diff', '--diff-filter=M',
|
||||
'--exit-code', old_tree, new_tree]).returncode
|
||||
|
||||
def print_diffstat(old_tree, new_tree):
|
||||
"""Print the diffstat between the two trees to stdout."""
|
||||
# We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
|
||||
# is expected to be viewed by the user, and only the former does nice things
|
||||
# like color and pagination.
|
||||
#
|
||||
# We also only print modified files since `new_tree` only contains the files
|
||||
# that were modified, so unmodified files would show as deleted without the
|
||||
# filter.
|
||||
return subprocess.run(['git', 'diff', '--diff-filter=M', '--exit-code',
|
||||
'--stat', old_tree, new_tree]).returncode
|
||||
|
||||
def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
||||
"""Apply the changes in `new_tree` to the working directory.
|
||||
|
@ -513,16 +617,16 @@ def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
|
|||
# better message, "Apply ... to index and worktree". This is not quite
|
||||
# right, since it won't be applied to the user's index, but oh well.
|
||||
with temporary_index_file(old_tree):
|
||||
subprocess.check_call(['git', 'checkout', '--patch', new_tree])
|
||||
subprocess.run(['git', 'checkout', '--patch', new_tree], check=True)
|
||||
index_tree = old_tree
|
||||
else:
|
||||
with temporary_index_file(new_tree):
|
||||
run('git', 'checkout-index', '-a', '-f')
|
||||
run('git', 'checkout-index', '-f', '--', *changed_files)
|
||||
return changed_files
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
stdin = kwargs.pop('stdin', to_bytes(''))
|
||||
stdin = kwargs.pop('stdin', '')
|
||||
verbose = kwargs.pop('verbose', True)
|
||||
strip = kwargs.pop('strip', True)
|
||||
for name in kwargs:
|
||||
|
@ -576,4 +680,4 @@ def convert_string(bytes_input):
|
|||
return str(bytes_input)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(main())
|
||||
|
|
Loading…
Reference in New Issue