Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/cmake-single-platform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,12 @@ jobs:
- name: Test (MSVC)
if: matrix.toolchain == 'msvc'
shell: pwsh
run: ctest --test-dir build --build-config Release --output-on-failure
# llvm_generated_compile runs cl directly, so it needs the dev shell env.
run: |
$vs = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -property installationPath
Import-Module "$vs\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"
Enter-VsDevShell -VsInstallPath $vs -SkipAutomaticLocation -DevCmdArguments '-arch=x64 -host_arch=x64'
ctest --test-dir build --build-config Release --output-on-failure

# Serial: these tests share artifacts in the build directory and report
# an inconsistent subset of failures when run in parallel.
Expand Down
23 changes: 11 additions & 12 deletions src/backend/llvm/fifo_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,17 @@ Value *FunctionEmitter::externalFifoWrite(Value *address, Value *value,
builder_.CreateBr(partialInvoke);

builder_.SetInsertPoint(partialInvoke);
Value *partialContext = builder_.CreateLoad(
pointer, builder_.CreateStructGEP(servicesType, services, 2));
StructType *resultType = StructType::get(context_, {i64, i32, i32});
CallInst *partialResult = builder_.CreateCall(
FunctionType::get(resultType, {pointer, i32, i32, i64, i32, i64}, false),
partialCallback,
{partialContext, builder_.getInt32(current_pc_), address,
builder_.CreateZExtOrTrunc(value, i64), builder_.getInt32(width), elapsed});
partialResult->addFnAttr(Attribute::NoUnwind);
Value *partialStatus = builder_.CreateExtractValue(partialResult, 1);
Value *partialRemaining = builder_.CreateExtractValue(partialResult, 2);
builder_.CreateStore(partialRemaining, remainingOut);
// The service returns a 16-byte struct, which the C ABI returns in registers
// on SysV but through a hidden pointer on Win64. Call it through C.
FunctionCallee partialService = module_.getOrInsertFunction(
"moderngekko_write_fifo_partial",
FunctionType::get(i32, {pointer, i32, i32, i64, i32, i64, pointer}, false));
CallInst *partialStatus = builder_.CreateCall(
partialService,
{ctx_, builder_.getInt32(current_pc_), address,
builder_.CreateZExtOrTrunc(value, i64), builder_.getInt32(width), elapsed,
remainingOut});
partialStatus->addFnAttr(Attribute::NoUnwind);
builder_.CreateCondBr(
builder_.CreateICmpEQ(partialStatus, builder_.getInt32(ServiceFallback)),
fullInvoke, partialClassify, MDBuilder(context_).createBranchWeights(1, 2000));
Expand Down
10 changes: 10 additions & 0 deletions src/backend/native_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,16 @@ void emit_native_state_commit(FILE* out) {
" *remaining = result.remaining_cycles;\n"
" return result.status;\n"
"}\n"
"DOLRECOMP_COLD uint32_t moderngekko_write_fifo_partial(\n"
" const MGNativeRuntime* runtime, uint32_t pc, uint32_t address,\n"
" uint64_t value, uint32_t size, uint64_t elapsed,\n"
" uint32_t* remaining) {\n"
" const MGNativeServices* services = runtime->services;\n"
" MGNativeMemoryResult result = services->write_fifo_partial(\n"
" services->context, pc, address, value, size, elapsed);\n"
" *remaining = result.remaining_cycles;\n"
" return result.status;\n"
"}\n"
"#undef DOLRECOMP_COLD\n",
out);
}
76 changes: 43 additions & 33 deletions tests/modern_memory_service_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,39 +160,38 @@ static uint32_t should_intercept(void* opaque, uint32_t guest_pc)
return guest_pc == context->intercept_pc;
}

MGNativeExit raw_func_80003120(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80003120");
MGNativeExit raw_func_80003140(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80003140");
MGNativeExit raw_func_80003500(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80003500");
MGNativeExit raw_func_80003D40(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80003D40");
MGNativeExit raw_func_80003DA0(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80003DA0");
MGNativeExit raw_func_80004A00(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80004A00");
MGNativeExit raw_func_80004E00(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80004E00");
MGNativeExit raw_func_80005000(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80005000");
MGNativeExit raw_func_80004D00(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80004D00");
MGNativeExit raw_func_80004D20(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80004D20");
MGNativeExit raw_func_80004D40(const MGNativeRuntime*, const MGNativeState*,
uint64_t*, uint64_t*, uint64_t*,
uint32_t, uint32_t, uint32_t) __asm__("func_80004D40");
// Bind raw_func_X to the generated func_X symbol. GNU compilers take an asm
// label, which needs the platform symbol prefix; MSVC has no asm labels, so it
// gets a linker alias instead.
#define MG_STRINGIFY2(x) #x
#define MG_STRINGIFY(x) MG_STRINGIFY2(x)
#if defined(_MSC_VER) && !defined(__clang__)
#define MG_RAW_LABEL(addr)
#define MG_RAW_ALIAS(addr) \
__pragma(comment(linker, "/alternatename:raw_func_" #addr "=func_" #addr))
#else
#define MG_RAW_LABEL(addr) \
__asm__(MG_STRINGIFY(__USER_LABEL_PREFIX__) "func_" #addr)
#define MG_RAW_ALIAS(addr)
#endif
#define MG_DECLARE_RAW(addr) \
MGNativeExit raw_func_##addr(const MGNativeRuntime*, const MGNativeState*, \
uint64_t*, uint64_t*, uint64_t*, \
uint32_t, uint32_t, uint32_t) \
MG_RAW_LABEL(addr); \
MG_RAW_ALIAS(addr)

MG_DECLARE_RAW(80003120)
MG_DECLARE_RAW(80003140)
MG_DECLARE_RAW(80003500)
MG_DECLARE_RAW(80003D40)
MG_DECLARE_RAW(80003DA0)
MG_DECLARE_RAW(80004A00)
MG_DECLARE_RAW(80004E00)
MG_DECLARE_RAW(80005000)
MG_DECLARE_RAW(80004D00)
MG_DECLARE_RAW(80004D20)
MG_DECLARE_RAW(80004D40)

static uint64_t staged_values[256];
static uint64_t staged_dirty[4];
Expand Down Expand Up @@ -591,6 +590,17 @@ uint32_t moderngekko_write_memory(const MGNativeRuntime* runtime, uint32_t pc,
return result.status;
}

uint32_t moderngekko_write_fifo_partial(const MGNativeRuntime* runtime,
uint32_t pc, uint32_t address,
uint64_t value, uint32_t size,
uint64_t elapsed,
uint32_t* remaining) {
MGNativeMemoryResult result = runtime->services->write_fifo_partial(
runtime->services->context, pc, address, value, size, elapsed);
*remaining = result.remaining_cycles;
return result.status;
}

static MGNativeState make_state(TestContext* context) {
MGNativeState state;
memset(&state, 0, sizeof(state));
Expand Down
Loading