Files
velzieandClaude Opus 5.5 afce666019 tools and tests: comments stand alone (no docs, stream or game history)
Comments only (and module docstrings) in the tools and tests that stay in
gex and feed no build cache key: gexcc, the native-libs scripts, the GL
and frame tools, and the 32-bit Wine Makefile, runner and pin lists. Drop
docs/ and section references and plan labels, name W3/W64 as 32-bit and
64-bit Wine, and drop the hosts, dates and games fixes were found with.
Files changed by others in the last two days are left for a later round;
the scripts hashed into the Wine dists' keys are on their own branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_40f26e8988a74ea098d8bc80ff68287c
2026-10-21 17:31:52 -04:00

86 lines
3.5 KiB
C++

// GexPoll: safepoint polls in native wasm32 code.
//
// gex delivers a signal to a native program at a system call's return:
// it cannot interrupt running wasm. A loop that
// makes no system calls (a computation, a busy wait, a test that relies on
// alarm() to stop it) would never see SIGALRM, SIGINT or SIGTERM. Translated
// code polls at back-edges (tier-1's safepoint countdown); this pass gives
// gexcc-built code the same: at every loop header
//
// if (--__gex_poll_counter < 0) __gex_poll();
//
// __gex_poll_counter is a wasm global, and so per thread (one module
// instance per thread). __gex_poll (tools/gexcc/rt/poll.c) re-arms it and
// makes one cheap system call, at whose return any pending signal is
// delivered and the kernel's interval timers are checked.
//
// Run at the end of the optimisation pipeline (after inlining and loop
// transforms), as GexIcall is: -fpass-plugin=GexPoll.so.
#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Plugins/PassPlugin.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
using namespace llvm;
namespace {
struct GexPoll : PassInfoMixin<GexPoll> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM) {
auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
LLVMContext &C = M.getContext();
Type *I32 = Type::getInt32Ty(C);
// the wasm global (address space 1), defined in rt/poll.s
GlobalVariable *Ctr = M.getNamedGlobal("__gex_poll_counter");
FunctionCallee Poll;
bool changed = false;
for (Function &F : M) {
if (F.isDeclaration() || F.getName().starts_with("__gex_"))
continue;
LoopInfo &LI = FAM.getResult<LoopAnalysis>(F);
SmallVector<BasicBlock *, 8> headers;
for (Loop *L : LI.getLoopsInPreorder())
headers.push_back(L->getHeader());
if (headers.empty())
continue;
if (!Ctr) {
Ctr = new GlobalVariable(M, I32, false, GlobalValue::ExternalLinkage, nullptr, "__gex_poll_counter",
nullptr, GlobalValue::NotThreadLocal, 1);
Poll = M.getOrInsertFunction("__gex_poll", FunctionType::get(Type::getVoidTy(C), false));
if (auto *PF = dyn_cast<Function>(Poll.getCallee()))
PF->addFnAttr(Attribute::Cold);
} else if (!Poll) {
Poll = M.getOrInsertFunction("__gex_poll", FunctionType::get(Type::getVoidTy(C), false));
}
for (BasicBlock *H : headers) {
Instruction *At = &*H->getFirstInsertionPt();
IRBuilder<> B(At);
Value *V = B.CreateLoad(I32, Ctr, "gexpoll");
Value *N = B.CreateSub(V, ConstantInt::get(I32, 1));
B.CreateStore(N, Ctr);
Value *Due = B.CreateICmpSLT(N, ConstantInt::get(I32, 0));
MDNode *W = MDBuilder(C).createBranchWeights(1, 1 << 20);
Instruction *Then = SplitBlockAndInsertIfThen(Due, At, false, W);
IRBuilder<> T(Then);
T.CreateCall(Poll);
}
changed = true;
FAM.invalidate(F, PreservedAnalyses::none());
}
return changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
}
};
} // namespace
extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "GexPoll", "0.1", [](PassBuilder &PB) {
PB.registerOptimizerLastEPCallback(
[](ModulePassManager &MPM, OptimizationLevel, ThinOrFullLTOPhase) { MPM.addPass(GexPoll()); });
}};
}