Skip to content

Commit 4e1c5af

Browse files
EgorBoCopilot
andcommitted
JIT: stop emitting x86 per-register write barrier helpers
Match what x64 and other targets already do: always emit a call to the unified CORINFO_HELP_ASSIGN_REF / CORINFO_HELP_CHECKED_ASSIGN_REF helper instead of selecting between per-source-register specialized helpers (CORINFO_HELP_ASSIGN_REF_EAX, etc.). The runtime universal helper already has the narrow ABI: ecx = dst, edx = src, only eax/edx trashed, all callee-saved registers and ecx preserved. The JIT continues to use RBM_CALLEE_TRASH_WRITEBARRIER (RBM_EAX | RBM_EDX) as the kill set, so it knows what's preserved across the call. This follows the same JIT-only first-step pattern as #128542 (Remove CORINFO_HELP_ASSIGN_BYREF). The enum values, jithelpers.h, readytorunhelpers.h, R2R format, runtime asm helpers (jithelp.asm/S, WriteBarriers.asm/S), jitinterfacex86.cpp, excep.cpp, and AOT tools are intentionally left alone for a follow-up cleanup similar to #128687. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e21a4c3 commit 4e1c5af

12 files changed

Lines changed: 8 additions & 226 deletions

src/coreclr/jit/codegen.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,6 @@ class CodeGen final : public CodeGenInterface
12701270
regNumber targetReg,
12711271
GenTreeIndir* indir,
12721272
bool* needsBarrier);
1273-
bool genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarrierForm, GenTree* addr, GenTree* data);
12741273
GenTree* getCallTarget(const GenTreeCall* call, CORINFO_METHOD_HANDLE* methHnd);
12751274
regNumber getCallIndirectionCellReg(GenTreeCall* call);
12761275
void genCall(GenTreeCall* call);

src/coreclr/jit/codegencommon.cpp

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -925,23 +925,6 @@ regMaskTP Compiler::compHelperCallKillSet(CorInfoHelpFunc helper)
925925
case CORINFO_HELP_PROF_FCN_TAILCALL:
926926
return RBM_PROFILER_TAILCALL_TRASH;
927927

928-
#ifdef TARGET_X86
929-
case CORINFO_HELP_ASSIGN_REF_EAX:
930-
case CORINFO_HELP_ASSIGN_REF_ECX:
931-
case CORINFO_HELP_ASSIGN_REF_EBX:
932-
case CORINFO_HELP_ASSIGN_REF_EBP:
933-
case CORINFO_HELP_ASSIGN_REF_ESI:
934-
case CORINFO_HELP_ASSIGN_REF_EDI:
935-
936-
case CORINFO_HELP_CHECKED_ASSIGN_REF_EAX:
937-
case CORINFO_HELP_CHECKED_ASSIGN_REF_ECX:
938-
case CORINFO_HELP_CHECKED_ASSIGN_REF_EBX:
939-
case CORINFO_HELP_CHECKED_ASSIGN_REF_EBP:
940-
case CORINFO_HELP_CHECKED_ASSIGN_REF_ESI:
941-
case CORINFO_HELP_CHECKED_ASSIGN_REF_EDI:
942-
return RBM_EDX;
943-
#endif
944-
945928
case CORINFO_HELP_STOP_FOR_GC:
946929
return RBM_STOP_FOR_GC_TRASH;
947930

@@ -2663,55 +2646,6 @@ void CodeGen::genReportEHClauses(EHClauseInfo* clauses)
26632646
}
26642647
}
26652648

2666-
#ifndef TARGET_WASM
2667-
2668-
//----------------------------------------------------------------------
2669-
// genUseOptimizedWriteBarriers: Determine if an optimized write barrier
2670-
// helper should be used.
2671-
//
2672-
// Arguments:
2673-
// wbf - The WriteBarrierForm of the write (GT_STOREIND) that is happening.
2674-
//
2675-
// Return Value:
2676-
// true if an optimized write barrier helper should be used, false otherwise.
2677-
// Note: only x86 implements register-specific source optimized write
2678-
// barriers currently.
2679-
//
2680-
bool CodeGenInterface::genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf)
2681-
{
2682-
#if defined(TARGET_X86) && NOGC_WRITE_BARRIERS
2683-
return true;
2684-
#else
2685-
return false;
2686-
#endif
2687-
}
2688-
2689-
//----------------------------------------------------------------------
2690-
// genUseOptimizedWriteBarriers: Determine if an optimized write barrier
2691-
// helper should be used.
2692-
//
2693-
// This has the same functionality as the version of
2694-
// genUseOptimizedWriteBarriers that takes a WriteBarrierForm, but avoids
2695-
// determining what the required write barrier form is, if possible.
2696-
//
2697-
// Arguments:
2698-
// store - the GT_STOREIND node
2699-
//
2700-
// Return Value:
2701-
// true if an optimized write barrier helper should be used, false otherwise.
2702-
// Note: only x86 implements register-specific source optimized write
2703-
// barriers currently.
2704-
//
2705-
bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTreeStoreInd* store)
2706-
{
2707-
#if defined(TARGET_X86) && NOGC_WRITE_BARRIERS
2708-
return true;
2709-
#else
2710-
return false;
2711-
#endif
2712-
}
2713-
#endif // !TARGET_WASM
2714-
27152649
//----------------------------------------------------------------------
27162650
// genWriteBarrierHelperForWriteBarrierForm: Given a write barrier form
27172651
// return the corresponding helper.
@@ -2722,9 +2656,6 @@ bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTreeStoreInd* store)
27222656
// Return Value:
27232657
// Write barrier helper to use.
27242658
//
2725-
// Note: do not call this function to get an optimized write barrier helper (e.g.,
2726-
// for x86).
2727-
//
27282659
CorInfoHelpFunc CodeGenInterface::genWriteBarrierHelperForWriteBarrierForm(GCInfo::WriteBarrierForm wbf)
27292660
{
27302661
INDEBUG(genWriteBarrierUsed = true);

src/coreclr/jit/codegeninterface.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ class CodeGenInterface
238238
TreeLifeUpdater<true>* treeLifeUpdater;
239239

240240
public:
241-
bool genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf);
242-
bool genUseOptimizedWriteBarriers(GenTreeStoreInd* store);
243241
CorInfoHelpFunc genWriteBarrierHelperForWriteBarrierForm(GCInfo::WriteBarrierForm wbf);
244242

245243
#ifdef DEBUG

src/coreclr/jit/codegenxarch.cpp

Lines changed: 0 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -5325,11 +5325,6 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
53255325
// Consume both registers so that any copies of interfering registers are taken care of.
53265326
genConsumeOperands(tree);
53275327

5328-
if (genEmitOptimizedGCWriteBarrier(writeBarrierForm, addr, data))
5329-
{
5330-
return;
5331-
}
5332-
53335328
// At this point, we should not have any interference.
53345329
// That is, 'data' must not be in REG_WRITE_BARRIER_DST, as that is where 'addr' must go.
53355330
noway_assert(data->GetRegNum() != REG_WRITE_BARRIER_DST);
@@ -5668,99 +5663,6 @@ void CodeGen::genCodeForSwap(GenTreeOp* tree)
56685663
gcInfo.gcMarkRegPtrVal(oldOp1Reg, type2);
56695664
}
56705665

5671-
//------------------------------------------------------------------------
5672-
// genEmitOptimizedGCWriteBarrier: Generate write barrier store using the optimized
5673-
// helper functions.
5674-
//
5675-
// Arguments:
5676-
// writeBarrierForm - the write barrier form to use
5677-
// addr - the address at which to do the store
5678-
// data - the data to store
5679-
//
5680-
// Return Value:
5681-
// true if an optimized write barrier form was used, false if not. If this
5682-
// function returns false, the caller must emit a "standard" write barrier.
5683-
5684-
bool CodeGen::genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarrierForm, GenTree* addr, GenTree* data)
5685-
{
5686-
assert(writeBarrierForm != GCInfo::WBF_NoBarrier);
5687-
5688-
#if defined(TARGET_X86) && NOGC_WRITE_BARRIERS
5689-
if (!genUseOptimizedWriteBarriers(writeBarrierForm))
5690-
{
5691-
return false;
5692-
}
5693-
5694-
const static int regToHelper[2][8] = {
5695-
// If the target is known to be in managed memory
5696-
{
5697-
CORINFO_HELP_ASSIGN_REF_EAX, // EAX
5698-
CORINFO_HELP_ASSIGN_REF_ECX, // ECX
5699-
-1, // EDX (always the target address)
5700-
CORINFO_HELP_ASSIGN_REF_EBX, // EBX
5701-
-1, // ESP
5702-
CORINFO_HELP_ASSIGN_REF_EBP, // EBP
5703-
CORINFO_HELP_ASSIGN_REF_ESI, // ESI
5704-
CORINFO_HELP_ASSIGN_REF_EDI, // EDI
5705-
},
5706-
5707-
// Don't know if the target is in managed memory
5708-
{
5709-
CORINFO_HELP_CHECKED_ASSIGN_REF_EAX, // EAX
5710-
CORINFO_HELP_CHECKED_ASSIGN_REF_ECX, // ECX
5711-
-1, // EDX (always the target address)
5712-
CORINFO_HELP_CHECKED_ASSIGN_REF_EBX, // EBX
5713-
-1, // ESP
5714-
CORINFO_HELP_CHECKED_ASSIGN_REF_EBP, // EBP
5715-
CORINFO_HELP_CHECKED_ASSIGN_REF_ESI, // ESI
5716-
CORINFO_HELP_CHECKED_ASSIGN_REF_EDI, // EDI
5717-
},
5718-
};
5719-
5720-
noway_assert(regToHelper[0][REG_EAX] == CORINFO_HELP_ASSIGN_REF_EAX);
5721-
noway_assert(regToHelper[0][REG_ECX] == CORINFO_HELP_ASSIGN_REF_ECX);
5722-
noway_assert(regToHelper[0][REG_EBX] == CORINFO_HELP_ASSIGN_REF_EBX);
5723-
noway_assert(regToHelper[0][REG_ESP] == -1);
5724-
noway_assert(regToHelper[0][REG_EBP] == CORINFO_HELP_ASSIGN_REF_EBP);
5725-
noway_assert(regToHelper[0][REG_ESI] == CORINFO_HELP_ASSIGN_REF_ESI);
5726-
noway_assert(regToHelper[0][REG_EDI] == CORINFO_HELP_ASSIGN_REF_EDI);
5727-
5728-
noway_assert(regToHelper[1][REG_EAX] == CORINFO_HELP_CHECKED_ASSIGN_REF_EAX);
5729-
noway_assert(regToHelper[1][REG_ECX] == CORINFO_HELP_CHECKED_ASSIGN_REF_ECX);
5730-
noway_assert(regToHelper[1][REG_EBX] == CORINFO_HELP_CHECKED_ASSIGN_REF_EBX);
5731-
noway_assert(regToHelper[1][REG_ESP] == -1);
5732-
noway_assert(regToHelper[1][REG_EBP] == CORINFO_HELP_CHECKED_ASSIGN_REF_EBP);
5733-
noway_assert(regToHelper[1][REG_ESI] == CORINFO_HELP_CHECKED_ASSIGN_REF_ESI);
5734-
noway_assert(regToHelper[1][REG_EDI] == CORINFO_HELP_CHECKED_ASSIGN_REF_EDI);
5735-
5736-
regNumber reg = data->GetRegNum();
5737-
noway_assert((reg != REG_ESP) && (reg != REG_OPTIMIZED_WRITE_BARRIER_DST));
5738-
5739-
// Generate the following code:
5740-
// lea edx, addr
5741-
// call write_barrier_helper_reg
5742-
5743-
// addr goes in REG_OPTIMIZED_WRITE_BARRIER_DST
5744-
genCopyRegIfNeeded(addr, REG_OPTIMIZED_WRITE_BARRIER_DST);
5745-
5746-
unsigned tgtAnywhere = 0;
5747-
if (writeBarrierForm != GCInfo::WBF_BarrierUnchecked)
5748-
{
5749-
tgtAnywhere = 1;
5750-
}
5751-
5752-
// Here we might want to call a modified version of genGCWriteBarrier() to get the benefit
5753-
// of the FEATURE_COUNT_GC_WRITE_BARRIERS code. For now, just emit the helper call directly.
5754-
genEmitHelperCall(regToHelper[tgtAnywhere][reg],
5755-
0, // argSize
5756-
EA_PTRSIZE); // retSize
5757-
5758-
return true;
5759-
#else // !defined(TARGET_X86) || !NOGC_WRITE_BARRIERS
5760-
return false;
5761-
#endif // !defined(TARGET_X86) || !NOGC_WRITE_BARRIERS
5762-
}
5763-
57645666
// Produce code for a GT_CALL node
57655667
void CodeGen::genCall(GenTreeCall* call)
57665668
{

src/coreclr/jit/lsrabuild.cpp

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -677,20 +677,9 @@ regMaskTP LinearScan::getKillSetForStoreInd(GenTreeStoreInd* tree)
677677
GCInfo::WriteBarrierForm writeBarrierForm = m_compiler->codeGen->gcInfo.gcIsWriteBarrierCandidate(tree);
678678
if (writeBarrierForm != GCInfo::WBF_NoBarrier)
679679
{
680-
if (m_compiler->codeGen->genUseOptimizedWriteBarriers(writeBarrierForm))
681-
{
682-
// We can't determine the exact helper to be used at this point, because it depends on
683-
// the allocated register for the `data` operand. However, all the (x86) optimized
684-
// helpers have the same kill set: EDX. And note that currently, only x86 can return
685-
// `true` for genUseOptimizedWriteBarriers().
686-
killMask = RBM_CALLEE_TRASH_NOGC;
687-
}
688-
else
689-
{
690-
// Figure out which helper we're going to use, and then get the kill set for that helper.
691-
CorInfoHelpFunc helper = m_compiler->codeGen->genWriteBarrierHelperForWriteBarrierForm(writeBarrierForm);
692-
killMask = m_compiler->compHelperCallKillSet(helper);
693-
}
680+
// Figure out which helper we're going to use, and then get the kill set for that helper.
681+
CorInfoHelpFunc helper = m_compiler->codeGen->genWriteBarrierHelperForWriteBarrierForm(writeBarrierForm);
682+
killMask = m_compiler->compHelperCallKillSet(helper);
694683
}
695684
return killMask;
696685
}
@@ -4542,20 +4531,6 @@ int LinearScan::BuildGCWriteBarrier(GenTree* tree)
45424531
SingleTypeRegSet addrCandidates = RBM_WRITE_BARRIER_DST.GetIntRegSet();
45434532
SingleTypeRegSet srcCandidates = RBM_WRITE_BARRIER_SRC.GetIntRegSet();
45444533

4545-
#if defined(TARGET_X86) && NOGC_WRITE_BARRIERS
4546-
4547-
bool useOptimizedWriteBarrierHelper = m_compiler->codeGen->genUseOptimizedWriteBarriers(tree->AsStoreInd());
4548-
if (useOptimizedWriteBarrierHelper)
4549-
{
4550-
// Special write barrier:
4551-
// op1 (addr) goes into REG_OPTIMIZED_WRITE_BARRIER_DST (rdx) and
4552-
// op2 (src) goes into any int register.
4553-
addrCandidates = RBM_OPTIMIZED_WRITE_BARRIER_DST.GetIntRegSet();
4554-
srcCandidates = RBM_OPTIMIZED_WRITE_BARRIER_SRC.GetIntRegSet();
4555-
}
4556-
4557-
#endif // defined(TARGET_X86) && NOGC_WRITE_BARRIERS
4558-
45594534
BuildUse(addr, addrCandidates);
45604535
BuildUse(src, srcCandidates);
45614536

src/coreclr/jit/targetamd64.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
// MAX_MULTIREG_COUNT - 1.
6262
#endif // !UNIX_AMD64_ABI
6363

64-
#define NOGC_WRITE_BARRIERS 0 // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
6564
#define USER_ARGS_COME_LAST 1
6665
#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target
6766
#ifdef UNIX_AMD64_ABI

src/coreclr/jit/targetarm.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
#define MAX_MULTIREG_COUNT 4 // Maximum number of registers defined by a single instruction (including calls).
3434
// This is also the maximum number of registers for a MultiReg node.
3535

36-
#define NOGC_WRITE_BARRIERS 0 // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
3736
#define USER_ARGS_COME_LAST 1
3837
#define TARGET_POINTER_SIZE 4 // equal to sizeof(void*) and the managed pointer size in bytes for this target
3938
#define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods

src/coreclr/jit/targetarm64.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
#define MAX_MULTIREG_COUNT 4 // Maximum number of registers defined by a single instruction (including calls).
3636
// This is also the maximum number of registers for a MultiReg node.
3737

38-
#define NOGC_WRITE_BARRIERS 1 // We have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
3938
#define USER_ARGS_COME_LAST 1
4039
#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target
4140
#define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods

src/coreclr/jit/targetloongarch64.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
#define MAX_MULTIREG_COUNT 2 // Maximum number of registers defined by a single instruction (including calls).
4141
// This is also the maximum number of registers for a MultiReg node.
4242

43-
#define NOGC_WRITE_BARRIERS 1 // We have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
4443
#define USER_ARGS_COME_LAST 1
4544
#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target
4645
#define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods

src/coreclr/jit/targetriscv64.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
#define MAX_MULTIREG_COUNT 2 // Maximum number of registers defined by a single instruction (including calls).
3636
// This is also the maximum number of registers for a MultiReg node.
3737

38-
#define NOGC_WRITE_BARRIERS 1 // We have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
3938
#define USER_ARGS_COME_LAST 1
4039
#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target
4140
#define ETW_EBP_FRAMED 1 // if 1 we cannot use REG_FP as a scratch register and must setup the frame pointer for most methods

0 commit comments

Comments
 (0)