summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Forstall <brucefo@microsoft.com>2018-04-03 18:50:45 -0700
committerGitHub <noreply@github.com>2018-04-03 18:50:45 -0700
commit4942bb10b3a7507c777e84f9b0f8770a99c982da (patch)
treed97d0add82b35fedd5c8e4112459aa83bd438a9a
parent3070e211eaa2e8dfdb0059de01a7c1693e589579 (diff)
parenta74067170f7c24b81c5b3794a6f3758f1aacc05b (diff)
downloadcoreclr-4942bb10b3a7507c777e84f9b0f8770a99c982da.tar.gz
coreclr-4942bb10b3a7507c777e84f9b0f8770a99c982da.tar.bz2
coreclr-4942bb10b3a7507c777e84f9b0f8770a99c982da.zip
Merge pull request #17372 from BruceForstall/ArmWriteBarrierTighten3
Tighten arm32/arm64 write barrier kill reg sets
-rw-r--r--src/jit/codegenarm64.cpp37
-rw-r--r--src/jit/codegencommon.cpp35
-rw-r--r--src/jit/codegenlegacy.cpp26
-rw-r--r--src/jit/emit.cpp5
-rw-r--r--src/jit/emitarm.cpp8
-rw-r--r--src/jit/emitarm64.cpp10
-rw-r--r--src/jit/gcinfo.cpp3
-rw-r--r--src/jit/gentree.cpp2
-rw-r--r--src/jit/importer.cpp6
-rw-r--r--src/jit/lsra.cpp6
-rw-r--r--src/jit/lsrabuild.cpp24
-rw-r--r--src/jit/optimizer.cpp2
-rw-r--r--src/jit/regset.cpp2
-rw-r--r--src/jit/target.h152
-rw-r--r--src/vm/arm/asmhelpers.asm13
15 files changed, 166 insertions, 165 deletions
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index efefbf10ad..ade7328db6 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -2757,7 +2757,7 @@ void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
}
//------------------------------------------------------------------------
-// genCodeForSwap: Produce code for a GT_CMPXCHG node.
+// genCodeForCmpXchg: Produce code for a GT_CMPXCHG node.
//
// Arguments:
// tree - the GT_CMPXCHG node
@@ -3006,41 +3006,16 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
// registers are taken care of.
genConsumeOperands(tree);
-#if NOGC_WRITE_BARRIERS
// At this point, we should not have any interference.
// That is, 'data' must not be in REG_WRITE_BARRIER_DST_BYREF,
// as that is where 'addr' must go.
noway_assert(data->gtRegNum != REG_WRITE_BARRIER_DST_BYREF);
- // 'addr' goes into x14 (REG_WRITE_BARRIER_DST_BYREF)
- if (addr->gtRegNum != REG_WRITE_BARRIER_DST_BYREF)
- {
- inst_RV_RV(INS_mov, REG_WRITE_BARRIER_DST_BYREF, addr->gtRegNum, addr->TypeGet());
- }
-
- // 'data' goes into x15 (REG_WRITE_BARRIER)
- if (data->gtRegNum != REG_WRITE_BARRIER)
- {
- inst_RV_RV(INS_mov, REG_WRITE_BARRIER, data->gtRegNum, data->TypeGet());
- }
-#else
- // At this point, we should not have any interference.
- // That is, 'data' must not be in REG_ARG_0,
- // as that is where 'addr' must go.
- noway_assert(data->gtRegNum != REG_ARG_0);
+ // 'addr' goes into x14 (REG_WRITE_BARRIER_DST)
+ genCopyRegIfNeeded(addr, REG_WRITE_BARRIER_DST);
- // addr goes in REG_ARG_0
- if (addr->gtRegNum != REG_ARG_0)
- {
- inst_RV_RV(INS_mov, REG_ARG_0, addr->gtRegNum, addr->TypeGet());
- }
-
- // data goes in REG_ARG_1
- if (data->gtRegNum != REG_ARG_1)
- {
- inst_RV_RV(INS_mov, REG_ARG_1, data->gtRegNum, data->TypeGet());
- }
-#endif // NOGC_WRITE_BARRIERS
+ // 'data' goes into x15 (REG_WRITE_BARRIER_SRC)
+ genCopyRegIfNeeded(data, REG_WRITE_BARRIER_SRC);
genGCWriteBarrier(tree, writeBarrierForm);
}
@@ -3116,6 +3091,8 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree)
//
void CodeGen::genCodeForSwap(GenTreeOp* tree)
{
+ assert(tree->OperIs(GT_SWAP));
+
// Swap is only supported for lclVar operands that are enregistered
// We do not consume or produce any registers. Both operands remain enregistered.
// However, the gc-ness may change.
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp
index c1bcff0eeb..f5dd308f85 100644
--- a/src/jit/codegencommon.cpp
+++ b/src/jit/codegencommon.cpp
@@ -635,7 +635,7 @@ void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bo
// helper - The helper being inquired about
//
// Return Value:
-// Mask of register kills -- registers whose value is no longer guaranteed to be the same.
+// Mask of register kills -- registers whose values are no longer guaranteed to be the same.
//
regMaskTP Compiler::compHelperCallKillSet(CorInfoHelpFunc helper)
{
@@ -645,7 +645,7 @@ regMaskTP Compiler::compHelperCallKillSet(CorInfoHelpFunc helper)
#if defined(_TARGET_AMD64_)
return RBM_RSI | RBM_RDI | RBM_CALLEE_TRASH_NOGC;
#elif defined(_TARGET_ARMARCH_)
- return RBM_WRITE_BARRIER_SRC_BYREF | RBM_WRITE_BARRIER_DST_BYREF | RBM_CALLEE_TRASH_NOGC;
+ return RBM_CALLEE_TRASH_WRITEBARRIER_BYREF;
#elif defined(_TARGET_X86_)
return RBM_ESI | RBM_EDI | RBM_ECX;
#else
@@ -653,6 +653,12 @@ regMaskTP Compiler::compHelperCallKillSet(CorInfoHelpFunc helper)
return RBM_CALLEE_TRASH;
#endif
+#if defined(_TARGET_ARMARCH_)
+ case CORINFO_HELP_ASSIGN_REF:
+ case CORINFO_HELP_CHECKED_ASSIGN_REF:
+ return RBM_CALLEE_TRASH_WRITEBARRIER;
+#endif
+
case CORINFO_HELP_PROF_FCN_ENTER:
#ifdef RBM_PROFILER_ENTER_TRASH
return RBM_PROFILER_ENTER_TRASH;
@@ -742,15 +748,20 @@ regMaskTP Compiler::compNoGCHelperCallKillSet(CorInfoHelpFunc helper)
return RBM_PROFILER_TAILCALL_TRASH;
#endif // defined(_TARGET_XARCH_)
+#if defined(_TARGET_X86_)
case CORINFO_HELP_ASSIGN_BYREF:
-#if defined(_TARGET_AMD64_)
- return RBM_CALLEE_TRASH_NOGC;
-#elif defined(_TARGET_X86_)
// This helper only trashes ECX.
return RBM_ECX;
-#elif defined(_TARGET_ARMARCH_)
- return RBM_CALLEE_TRASH_NOGC;
-#endif // defined(_TARGET_AMD64_)
+#endif // defined(_TARGET_X86_)
+
+#if defined(_TARGET_ARMARCH_)
+ case CORINFO_HELP_ASSIGN_BYREF:
+ return RBM_CALLEE_GCTRASH_WRITEBARRIER_BYREF;
+
+ case CORINFO_HELP_ASSIGN_REF:
+ case CORINFO_HELP_CHECKED_ASSIGN_REF:
+ return RBM_CALLEE_GCTRASH_WRITEBARRIER;
+#endif
default:
return RBM_CALLEE_TRASH_NOGC;
@@ -3969,8 +3980,8 @@ void CodeGen::genReportEH()
//
// Return Value:
// true if an optimized write barrier helper should be used, false otherwise.
-// Note: only x86 implements (register-specific source) optimized write
-// barriers currently).
+// Note: only x86 implements register-specific source optimized write
+// barriers currently.
//
bool CodeGenInterface::genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf)
{
@@ -3999,8 +4010,8 @@ bool CodeGenInterface::genUseOptimizedWriteBarriers(GCInfo::WriteBarrierForm wbf
//
// Return Value:
// true if an optimized write barrier helper should be used, false otherwise.
-// Note: only x86 implements (register-specific source) optimized write
-// barriers currently).
+// Note: only x86 implements register-specific source optimized write
+// barriers currently.
//
bool CodeGenInterface::genUseOptimizedWriteBarriers(GenTree* tgt, GenTree* assignVal)
{
diff --git a/src/jit/codegenlegacy.cpp b/src/jit/codegenlegacy.cpp
index 648a48162d..a2aeb5e779 100644
--- a/src/jit/codegenlegacy.cpp
+++ b/src/jit/codegenlegacy.cpp
@@ -2102,7 +2102,7 @@ regMaskTP CodeGen::genMakeAddrArrElem(GenTree* arrElem, GenTree* tree, regMaskTP
regMaskTP addrReg = RBM_NONE;
regMaskTP regNeed = RBM_ALLINT;
-#if FEATURE_WRITE_BARRIER && !NOGC_WRITE_BARRIERS
+#if !NOGC_WRITE_BARRIERS
// In CodeGen::WriteBarrier we set up ARG_1 followed by ARG_0
// since the arrObj participates in the lea/add instruction
// that computes ARG_0 we should avoid putting it in ARG_1
@@ -3108,8 +3108,6 @@ regMaskTP CodeGen::WriteBarrier(GenTree* tgt, GenTree* assignVal, regMaskTP tgtA
regMaskTP resultRegMask = RBM_NONE;
-#if FEATURE_WRITE_BARRIER
-
regNumber reg = assignVal->gtRegNum;
#if defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS
@@ -3340,13 +3338,6 @@ regMaskTP CodeGen::WriteBarrier(GenTree* tgt, GenTree* assignVal, regMaskTP tgtA
return resultRegMask;
}
#endif // defined(DEBUG) || !(defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS)
-
-#else // !FEATURE_WRITE_BARRIER
-
- NYI("FEATURE_WRITE_BARRIER unimplemented");
- return resultRegMask;
-
-#endif // !FEATURE_WRITE_BARRIER
}
#ifdef _TARGET_X86_
@@ -8877,8 +8868,7 @@ void CodeGen::genCodeForCopyObj(GenTree* tree, regMaskTP destReg)
CorInfoGCType gcTypeNext = TYPE_GC_NONE;
var_types type = TYP_I_IMPL;
-#if FEATURE_WRITE_BARRIER
- gcType = (CorInfoGCType)(*gcPtrs++);
+ gcType = (CorInfoGCType)(*gcPtrs++);
if (blkSize > TARGET_POINTER_SIZE)
gcTypeNext = (CorInfoGCType)(*gcPtrs);
@@ -8893,9 +8883,6 @@ void CodeGen::genCodeForCopyObj(GenTree* tree, regMaskTP destReg)
assert(regSrc == REG_ARG_1);
assert(regTemp == REG_R2);
}
-#else
- gcType = TYPE_GC_NONE;
-#endif // FEATURE_WRITE_BARRIER
blkSize -= TARGET_POINTER_SIZE;
@@ -10427,14 +10414,7 @@ void CodeGen::genCodeForTreeSmpOp(GenTree* tree, regMaskTP destReg, regMaskTP be
}
return;
-#else // !_TARGET_XARCH_
-
- case GT_LOCKADD:
- case GT_XADD:
- case GT_XCHG:
-
- NYI_ARM("LOCK instructions");
-#endif
+#endif // _TARGET_XARCH_
case GT_ARR_LENGTH:
{
diff --git a/src/jit/emit.cpp b/src/jit/emit.cpp
index 7a1d106f17..32524ea5ed 100644
--- a/src/jit/emit.cpp
+++ b/src/jit/emit.cpp
@@ -2335,15 +2335,12 @@ bool emitter::emitNoGChelper(unsigned IHX)
#endif
case CORINFO_HELP_ASSIGN_REF:
-
case CORINFO_HELP_CHECKED_ASSIGN_REF:
+ case CORINFO_HELP_ASSIGN_BYREF:
case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR:
-
case CORINFO_HELP_GETSHARED_NONGCSTATIC_BASE_NOCTOR:
- case CORINFO_HELP_ASSIGN_BYREF:
-
case CORINFO_HELP_INIT_PINVOKE_FRAME:
return true;
diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp
index db6dbdd66a..e5c98f9469 100644
--- a/src/jit/emitarm.cpp
+++ b/src/jit/emitarm.cpp
@@ -4467,12 +4467,8 @@ void emitter::emitIns_Call(EmitCallType callType,
{
assert(emitNoGChelper(Compiler::eeGetHelperNum(methHnd)));
- // This call will preserve the liveness of most registers
- //
- // - On the ARM the NOGC helpers will preserve all registers,
- // except for those listed in the RBM_CALLEE_TRASH_NOGC mask
-
- savedSet = RBM_ALLINT & ~RBM_CALLEE_TRASH_NOGC;
+ // Get the set of registers that this call kills and remove it from the saved set.
+ savedSet = RBM_ALLINT & ~emitComp->compNoGCHelperCallKillSet(Compiler::eeGetHelperNum(methHnd));
// In case of Leave profiler callback, we need to preserve liveness of REG_PROFILER_RET_SCRATCH
if (isProfLeaveCB)
diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp
index fe690d2abb..dfca2cdd00 100644
--- a/src/jit/emitarm64.cpp
+++ b/src/jit/emitarm64.cpp
@@ -7356,6 +7356,16 @@ void emitter::emitIns_Call(EmitCallType callType,
{
savedSet |= RBM_PROFILER_RET_SCRATCH;
}
+
+#ifdef DEBUG
+ if (emitComp->verbose)
+ {
+ printf("NOGC Call: savedSet=");
+ printRegMaskInt(savedSet);
+ emitDispRegSet(savedSet);
+ printf("\n");
+ }
+#endif
}
else
{
diff --git a/src/jit/gcinfo.cpp b/src/jit/gcinfo.cpp
index a91bef2629..6ed3f6d0df 100644
--- a/src/jit/gcinfo.cpp
+++ b/src/jit/gcinfo.cpp
@@ -231,8 +231,6 @@ void GCInfo::gcMarkRegPtrVal(regNumber reg, var_types type)
GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree* assignVal)
{
-#if FEATURE_WRITE_BARRIER
-
/* Are we storing a GC ptr? */
if (!varTypeIsGC(tgt->TypeGet()))
@@ -292,7 +290,6 @@ GCInfo::WriteBarrierForm GCInfo::gcIsWriteBarrierCandidate(GenTree* tgt, GenTree
}
assert(!"Missing case in gcIsWriteBarrierCandidate");
-#endif
return WBF_NoBarrier;
}
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index 10ef3738f1..4614df0e37 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -7456,7 +7456,7 @@ GenTree* Compiler::gtNewPutArgReg(var_types type, GenTree* arg, regNumber argReg
// Returns the newly created BitCast node.
//
// Notes:
-// The node is generated as GenTreeMultiRegOp on RyuJIT/armel, as GenTreeOp on all the other archs.
+// The node is generated as GenTreeMultiRegOp on RyuJIT/arm, as GenTreeOp on all the other archs.
//
GenTree* Compiler::gtNewBitCastNode(var_types type, GenTree* arg)
{
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index e87065310d..182a049820 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -19221,9 +19221,9 @@ bool Compiler::IsTargetIntrinsic(CorInfoIntrinsics intrinsicId)
bool Compiler::IsIntrinsicImplementedByUserCall(CorInfoIntrinsics intrinsicId)
{
- // Currently, if an math intrisic is not implemented by target-specific
- // intructions, it will be implemented by a System.Math call. In the
- // future, if we turn to implementing some of them with helper callers,
+ // Currently, if a math intrinsic is not implemented by target-specific
+ // instructions, it will be implemented by a System.Math call. In the
+ // future, if we turn to implementing some of them with helper calls,
// this predicate needs to be revisited.
return !IsTargetIntrinsic(intrinsicId);
}
diff --git a/src/jit/lsra.cpp b/src/jit/lsra.cpp
index 086be38566..66a665b2b2 100644
--- a/src/jit/lsra.cpp
+++ b/src/jit/lsra.cpp
@@ -6377,14 +6377,14 @@ void LinearScan::insertCopyOrReload(BasicBlock* block, GenTree* tree, unsigned m
}
// If the parent is a reload/copy node, then tree must be a multi-reg call node
- // that has already had one of its registers spilled. This is Because multi-reg
+ // that has already had one of its registers spilled. This is because multi-reg
// call node is the only node whose RefTypeDef positions get independently
// spilled or reloaded. It is possible that one of its RefTypeDef position got
// spilled and the next use of it requires it to be in a different register.
//
- // In this case set the ith position reg of reload/copy node to the reg allocated
+ // In this case set the i'th position reg of reload/copy node to the reg allocated
// for copy/reload refPosition. Essentially a copy/reload node will have a reg
- // for each multi-reg position of its child. If there is a valid reg in ith
+ // for each multi-reg position of its child. If there is a valid reg in i'th
// position of GT_COPY or GT_RELOAD node then the corresponding result of its
// child needs to be copied or reloaded to that reg.
if (parent->IsCopyOrReload())
diff --git a/src/jit/lsrabuild.cpp b/src/jit/lsrabuild.cpp
index 5023b13069..62692d587a 100644
--- a/src/jit/lsrabuild.cpp
+++ b/src/jit/lsrabuild.cpp
@@ -664,7 +664,8 @@ regMaskTP LinearScan::getKillSetForStoreInd(GenTreeStoreInd* tree)
{
// We can't determine the exact helper to be used at this point, because it depends on
// the allocated register for the `data` operand. However, all the (x86) optimized
- // helpers have the same kill set: EDX.
+ // helpers have the same kill set: EDX. And note that currently, only x86 can return
+ // `true` for genUseOptimizedWriteBarriers().
killMask = RBM_CALLEE_TRASH_NOGC;
}
else
@@ -3141,22 +3142,18 @@ void LinearScan::BuildGCWriteBarrier(GenTree* tree)
assert(info->dstCount == 0);
bool customSourceRegs = false;
-#if NOGC_WRITE_BARRIERS
-
#if defined(_TARGET_ARM64_)
- // For the NOGC JIT Helper calls
- //
- // the 'addr' goes into x14 (REG_WRITE_BARRIER_DST_BYREF)
- // the 'src' goes into x15 (REG_WRITE_BARRIER)
+
+ // the 'addr' goes into x14 (REG_WRITE_BARRIER_DST)
+ // the 'src' goes into x15 (REG_WRITE_BARRIER_SRC)
//
- addrInfo->info.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
- srcInfo->info.setSrcCandidates(this, RBM_WRITE_BARRIER);
+ addrInfo->info.setSrcCandidates(this, RBM_WRITE_BARRIER_DST);
+ srcInfo->info.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC);
customSourceRegs = true;
-#elif defined(_TARGET_X86_)
+#elif defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS
bool useOptimizedWriteBarrierHelper = compiler->codeGen->genUseOptimizedWriteBarriers(tree, src);
-
if (useOptimizedWriteBarrierHelper)
{
// Special write barrier:
@@ -3166,11 +3163,8 @@ void LinearScan::BuildGCWriteBarrier(GenTree* tree)
srcInfo->info.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC);
customSourceRegs = true;
}
-#else // !defined(_TARGET_X86_) && !defined(_TARGET_ARM64_)
-#error "NOGC_WRITE_BARRIERS is not supported"
-#endif // !defined(_TARGET_X86_)
-#endif // NOGC_WRITE_BARRIERS
+#endif // defined(_TARGET_X86_) && NOGC_WRITE_BARRIERS
if (!customSourceRegs)
{
diff --git a/src/jit/optimizer.cpp b/src/jit/optimizer.cpp
index ea8f6a58d8..bd3d29650f 100644
--- a/src/jit/optimizer.cpp
+++ b/src/jit/optimizer.cpp
@@ -5573,7 +5573,7 @@ void Compiler::optEnsureUniqueHead(unsigned loopInd, unsigned ambientWeight)
return CALLINT_SCL_INDIRS;
- case CORINFO_HELP_ASSIGN_STRUCT: // Not strictly needed as we don't use this in Jit32
+ case CORINFO_HELP_ASSIGN_STRUCT: // Not strictly needed as we don't use this
case CORINFO_HELP_MEMSET: // Not strictly needed as we don't make a GT_CALL with this
case CORINFO_HELP_MEMCPY: // Not strictly needed as we don't make a GT_CALL with this
case CORINFO_HELP_SETFIELDSTRUCT:
diff --git a/src/jit/regset.cpp b/src/jit/regset.cpp
index 4b3134d3bd..f37c422646 100644
--- a/src/jit/regset.cpp
+++ b/src/jit/regset.cpp
@@ -1902,11 +1902,13 @@ void RegSet::rsSpillRegs(regMaskTP regMask)
* for internal tree temps to live in
*/
+#ifdef LEGACY_BACKEND
extern const regNumber raRegTmpOrder[] = {REG_TMP_ORDER};
extern const regNumber rpRegTmpOrder[] = {REG_PREDICT_ORDER};
#if FEATURE_FP_REGALLOC
extern const regNumber raRegFltTmpOrder[] = {REG_FLT_TMP_ORDER};
#endif
+#endif // LEGACY_BACKEND
/*****************************************************************************
*
diff --git a/src/jit/target.h b/src/jit/target.h
index 5838cdda88..45bc1013a6 100644
--- a/src/jit/target.h
+++ b/src/jit/target.h
@@ -366,7 +366,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define ALIGN_SIMD_TYPES 1 // whether SIMD type locals are to be aligned
#endif // FEATURE_SIMD
- #define FEATURE_WRITE_BARRIER 1 // Generate the proper WriteBarrier calls for GC
#define FEATURE_FIXED_OUT_ARGS 0 // X86 uses push instructions to pass args
#define FEATURE_STRUCTPROMOTE 1 // JIT Optimization to promote fields of structs into registers
#define FEATURE_MULTIREG_STRUCT_PROMOTE 0 // True when we want to promote fields of a multireg struct into registers
@@ -510,11 +509,13 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_VAR_ORDER REG_EAX,REG_EDX,REG_ECX,REG_ESI,REG_EDI,REG_EBX
#define MAX_VAR_ORDER_SIZE 6
+
+#ifdef LEGACY_BACKEND
#define REG_TMP_ORDER REG_EAX,REG_EDX,REG_ECX,REG_EBX,REG_ESI,REG_EDI
- #define RBM_TMP_ORDER RBM_EAX,RBM_EDX,RBM_ECX,RBM_EBX,RBM_ESI,RBM_EDI
#define REG_TMP_ORDER_COUNT 6
#define REG_PREDICT_ORDER REG_EAX,REG_EDX,REG_ECX,REG_EBX,REG_ESI,REG_EDI
+#endif // LEGACY_BACKEND
// The order here is fixed: it must agree with an order assumed in eetwain...
#define REG_CALLEE_SAVED_ORDER REG_EDI,REG_ESI,REG_EBX,REG_EBP
@@ -743,7 +744,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define FEATURE_PARTIAL_SIMD_CALLEE_SAVE 1 // Whether SIMD registers are partially saved at calls
#endif // !UNIX_AMD64_ABI
#endif
- #define FEATURE_WRITE_BARRIER 1 // Generate the WriteBarrier calls for GC (currently not the x86-style register-customized barriers)
#define FEATURE_FIXED_OUT_ARGS 1 // Preallocate the outgoing arg area in the prolog
#define FEATURE_STRUCTPROMOTE 1 // JIT Optimization to promote fields of structs into registers
#define FEATURE_MULTIREG_STRUCT_PROMOTE 0 // True when we want to promote fields of a multireg struct into registers
@@ -770,11 +770,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define MAX_RET_REG_COUNT 1 // Maximum registers used to return a value.
#endif // !UNIX_AMD64_ABI
-#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
#define NOGC_WRITE_BARRIERS 0 // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
-#else
- #define NOGC_WRITE_BARRIERS 0 // Do not modify this -- modify the definition above. (If we're not using ASM barriers we definitely don't have NOGC barriers).
-#endif
#define USER_ARGS_COME_LAST 1
#define EMIT_TRACK_STACK_DEPTH 1
#define TARGET_POINTER_SIZE 8 // equal to sizeof(void*) and the managed pointer size in bytes for this target
@@ -882,17 +878,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_VAR_ORDER_FLT REG_XMM0,REG_XMM1,REG_XMM2,REG_XMM3,REG_XMM4,REG_XMM5,REG_XMM6,REG_XMM7,REG_XMM8,REG_XMM9,REG_XMM10,REG_XMM11,REG_XMM12,REG_XMM13,REG_XMM14,REG_XMM15
#ifdef UNIX_AMD64_ABI
- #define REG_TMP_ORDER REG_EAX,REG_EDI,REG_ESI,REG_EDX,REG_ECX,REG_EBX,REG_ETW_FRAMED_EBP_LIST \
- REG_R8,REG_R9,REG_R10,REG_R11,REG_R14,REG_R15,REG_R12,REG_R13
-#else // !UNIX_AMD64_ABI
- #define MAX_VAR_ORDER_SIZE (14 + REG_ETW_FRAMED_EBP_COUNT)
- #define REG_TMP_ORDER REG_EAX,REG_EDX,REG_ECX,REG_EBX,REG_ESI,REG_EDI,REG_ETW_FRAMED_EBP_LIST \
- REG_R8,REG_R9,REG_R10,REG_R11,REG_R14,REG_R15,REG_R12,REG_R13
-#endif // !UNIX_AMD64_ABI
-
-#ifdef UNIX_AMD64_ABI
- #define REG_PREDICT_ORDER REG_EAX,REG_EDI,REG_ESI,REG_EDX,REG_ECX,REG_EBX,REG_ETW_FRAMED_EBP_LIST \
- REG_R8,REG_R9,REG_R10,REG_R11,REG_R14,REG_R15,REG_R12,REG_R13
#define CNT_CALLEE_SAVED (5 + REG_ETW_FRAMED_EBP_COUNT)
#define CNT_CALLEE_TRASH (9)
#define CNT_CALLEE_ENREG (CNT_CALLEE_SAVED)
@@ -903,9 +888,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_CALLEE_SAVED_ORDER REG_EBX,REG_ETW_FRAMED_EBP_LIST REG_R12,REG_R13,REG_R14,REG_R15
#define RBM_CALLEE_SAVED_ORDER RBM_EBX,RBM_ETW_FRAMED_EBP_LIST RBM_R12,RBM_R13,RBM_R14,RBM_R15
#else // !UNIX_AMD64_ABI
- #define REG_TMP_ORDER_COUNT (14 + REG_ETW_FRAMED_EBP_COUNT)
- #define REG_PREDICT_ORDER REG_EAX,REG_EDX,REG_ECX,REG_EBX,REG_ESI,REG_EDI,REG_ETW_FRAMED_EBP_LIST \
- REG_R8,REG_R9,REG_R10,REG_R11,REG_R14,REG_R15,REG_R12,REG_R13
#define CNT_CALLEE_SAVED (7 + REG_ETW_FRAMED_EBP_COUNT)
#define CNT_CALLEE_TRASH (7)
#define CNT_CALLEE_ENREG (CNT_CALLEE_SAVED)
@@ -969,11 +951,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_JUMP_THUNK_PARAM REG_EAX
#define RBM_JUMP_THUNK_PARAM RBM_EAX
-#if NOGC_WRITE_BARRIERS
- #define REG_WRITE_BARRIER REG_EDX
- #define RBM_WRITE_BARRIER RBM_EDX
-#endif
-
// Register to be used for emitting helper calls whose call target is an indir of an
// absolute memory address in case of Rel32 overflow i.e. a data address could not be
// encoded as PC-relative 32-bit offset.
@@ -991,12 +968,10 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
// GenericPInvokeCalliHelper VASigCookie Parameter
#define REG_PINVOKE_COOKIE_PARAM REG_R11
#define RBM_PINVOKE_COOKIE_PARAM RBM_R11
- #define PREDICT_REG_PINVOKE_COOKIE_PARAM PREDICT_REG_R11
// GenericPInvokeCalliHelper unmanaged target Parameter
#define REG_PINVOKE_TARGET_PARAM REG_R10
#define RBM_PINVOKE_TARGET_PARAM RBM_R10
- #define PREDICT_REG_PINVOKE_TARGET_PARAM PREDICT_REG_R10
// IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM)
#define REG_SECRET_STUB_PARAM REG_R10
@@ -1188,7 +1163,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define CPBLK_UNROLL_LIMIT 32 // Upper bound to let the code generator to loop unroll CpBlk.
#define INITBLK_UNROLL_LIMIT 32 // Upper bound to let the code generator to loop unroll InitBlk.
- #define FEATURE_WRITE_BARRIER 1 // Generate the proper WriteBarrier calls for GC
#define FEATURE_FIXED_OUT_ARGS 1 // Preallocate the outgoing arg area in the prolog
#define FEATURE_STRUCTPROMOTE 1 // JIT Optimization to promote fields of structs into registers
#define FEATURE_MULTIREG_STRUCT_PROMOTE 0 // True when we want to promote fields of a multireg struct into registers
@@ -1204,11 +1178,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define MAX_ARG_REG_COUNT 4 // Maximum registers used to pass a single argument in multiple registers. (max is 4 floats or doubles using an HFA)
#define MAX_RET_REG_COUNT 4 // Maximum registers used to return a value.
-#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
#define NOGC_WRITE_BARRIERS 0 // We DO-NOT have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
-#else
- #define NOGC_WRITE_BARRIERS 0 // Do not modify this -- modify the definition above. (If we're not using ASM barriers we definitely don't have NOGC barriers).
-#endif
#define USER_ARGS_COME_LAST 1
#define EMIT_TRACK_STACK_DEPTH 1 // This is something of a workaround. For both ARM and AMD64, the frame size is fixed, so we don't really
// need to track stack depth, but this is currently necessary to get GC information reported at call sites.
@@ -1247,8 +1217,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_DEFAULT_HELPER_CALL_TARGET REG_R12
#define RBM_DEFAULT_HELPER_CALL_TARGET RBM_R12
- #define RBM_CALLEE_TRASH_NOGC (RBM_R2|RBM_R3|RBM_LR|RBM_DEFAULT_HELPER_CALL_TARGET)
-
#define REG_FASTTAILCALL_TARGET REG_R12 // Target register for fast tail call
#define RBM_FASTTAILCALL_TARGET RBM_R12
@@ -1268,6 +1236,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
REG_F24, REG_F25, REG_F26, REG_F27, \
REG_F28, REG_F29, REG_F30, REG_F31,
+#ifdef LEGACY_BACKEND
#define MAX_VAR_ORDER_SIZE 32
#define REG_TMP_ORDER REG_R3,REG_R2,REG_R1,REG_R0, REG_R4,REG_R5,REG_R6,REG_R7,\
@@ -1287,6 +1256,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_PREDICT_ORDER REG_LR,REG_R12,REG_R3,REG_R2,REG_R1,REG_R0, \
REG_R7,REG_R6,REG_R5,REG_R4,REG_R8,REG_R9,REG_R10
+#endif // LEGACY_BACKEND
#define RBM_LOW_REGS (RBM_R0|RBM_R1|RBM_R2|RBM_R3|RBM_R4|RBM_R5|RBM_R6|RBM_R7)
#define RBM_HIGH_REGS (RBM_R8|RBM_R9|RBM_R10|RBM_R11|RBM_R12|RBM_SP|RBM_LR|RBM_PC)
@@ -1371,27 +1341,60 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_JUMP_THUNK_PARAM REG_R12
#define RBM_JUMP_THUNK_PARAM RBM_R12
-#if NOGC_WRITE_BARRIERS
- #define REG_WRITE_BARRIER REG_R1
- #define RBM_WRITE_BARRIER RBM_R1
-#endif
+ // ARM write barrier ABI (see vm\arm\asmhelpers.asm, vm\arm\asmhelpers.S):
+ // CORINFO_HELP_ASSIGN_REF (JIT_WriteBarrier), CORINFO_HELP_CHECKED_ASSIGN_REF (JIT_CheckedWriteBarrier):
+ // On entry:
+ // r0: the destination address (LHS of the assignment)
+ // r1: the object reference (RHS of the assignment)
+ // On exit:
+ // r0: trashed
+ // r3: trashed
+ // CORINFO_HELP_ASSIGN_BYREF (JIT_ByRefWriteBarrier):
+ // On entry:
+ // r0: the destination address (object reference written here)
+ // r1: the source address (points to object reference to write)
+ // On exit:
+ // r0: incremented by 4
+ // r1: incremented by 4
+ // r2: trashed
+ // r3: trashed
+
+ #define REG_WRITE_BARRIER_DST_BYREF REG_ARG_0
+ #define RBM_WRITE_BARRIER_DST_BYREF RBM_ARG_0
- //In the ARM case, registers of write barrier use the normal argument registers.
#define REG_WRITE_BARRIER_SRC_BYREF REG_ARG_1
#define RBM_WRITE_BARRIER_SRC_BYREF RBM_ARG_1
- #define REG_WRITE_BARRIER_DST_BYREF REG_ARG_0
- #define RBM_WRITE_BARRIER_DST_BYREF RBM_ARG_0
+ #define RBM_CALLEE_TRASH_NOGC (RBM_R2|RBM_R3|RBM_LR|RBM_DEFAULT_HELPER_CALL_TARGET)
+
+ // Registers killed by CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF.
+ #define RBM_CALLEE_TRASH_WRITEBARRIER (RBM_R0|RBM_R3|RBM_LR|RBM_DEFAULT_HELPER_CALL_TARGET)
+
+ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF.
+ #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_WRITEBARRIER
+
+ // Registers killed by CORINFO_HELP_ASSIGN_BYREF.
+ #define RBM_CALLEE_TRASH_WRITEBARRIER_BYREF (RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF | RBM_CALLEE_TRASH_NOGC)
+
+ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_BYREF.
+ // Note that r0 and r1 are still valid byref pointers after this helper call, despite their value being changed.
+ #define RBM_CALLEE_GCTRASH_WRITEBARRIER_BYREF RBM_CALLEE_TRASH_NOGC
// GenericPInvokeCalliHelper VASigCookie Parameter
#define REG_PINVOKE_COOKIE_PARAM REG_R4
#define RBM_PINVOKE_COOKIE_PARAM RBM_R4
+
+#ifdef LEGACY_BACKEND
#define PREDICT_REG_PINVOKE_COOKIE_PARAM PREDICT_REG_R4
+#endif // LEGACY_BACKEND
// GenericPInvokeCalliHelper unmanaged target Parameter
#define REG_PINVOKE_TARGET_PARAM REG_R12
#define RBM_PINVOKE_TARGET_PARAM RBM_R12
+
+#ifdef LEGACY_BACKEND
#define PREDICT_REG_PINVOKE_TARGET_PARAM PREDICT_REG_R12
+#endif // LEGACY_BACKEND
// IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM)
#define REG_SECRET_STUB_PARAM REG_R12
@@ -1546,7 +1549,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define FEATURE_PARTIAL_SIMD_CALLEE_SAVE 1 // Whether SIMD registers are partially saved at calls
#endif // FEATURE_SIMD
- #define FEATURE_WRITE_BARRIER 1 // Generate the proper WriteBarrier calls for GC
#define FEATURE_FIXED_OUT_ARGS 1 // Preallocate the outgoing arg area in the prolog
#define FEATURE_STRUCTPROMOTE 1 // JIT Optimization to promote fields of structs into registers
#define FEATURE_MULTIREG_STRUCT_PROMOTE 1 // True when we want to promote fields of a multireg struct into registers
@@ -1562,11 +1564,7 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define MAX_ARG_REG_COUNT 4 // Maximum registers used to pass a single argument in multiple registers. (max is 4 floats or doubles using an HFA)
#define MAX_RET_REG_COUNT 4 // Maximum registers used to return a value.
-#ifdef FEATURE_USE_ASM_GC_WRITE_BARRIERS
#define NOGC_WRITE_BARRIERS 1 // We have specialized WriteBarrier JIT Helpers that DO-NOT trash the RBM_CALLEE_TRASH registers
-#else
- #define NOGC_WRITE_BARRIERS 0 // Do not modify this -- modify the definition above. (If we're not using ASM barriers we definitely don't have NOGC barriers).
-#endif
#define USER_ARGS_COME_LAST 1
#define EMIT_TRACK_STACK_DEPTH 1 // This is something of a workaround. For both ARM and AMD64, the frame size is fixed, so we don't really
// need to track stack depth, but this is currently necessary to get GC information reported at call sites.
@@ -1608,9 +1606,6 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_DEFAULT_HELPER_CALL_TARGET REG_R12
#define RBM_DEFAULT_HELPER_CALL_TARGET RBM_R12
- // REVIEW: why does arm64 RBM_CALLEE_TRASH_NOGC include IP1? The JIT_ByRefWriteBarrier only trashes r12 and r15.
- #define RBM_CALLEE_TRASH_NOGC (RBM_R12|RBM_R15|RBM_IP1|RBM_DEFAULT_HELPER_CALL_TARGET)
-
#define REG_FASTTAILCALL_TARGET REG_IP0 // Target register for fast tail call
#define RBM_FASTTAILCALL_TARGET RBM_IP0
@@ -1679,26 +1674,65 @@ typedef unsigned short regPairNoSmall; // arm: need 12 bits
#define REG_JUMP_THUNK_PARAM REG_R12
#define RBM_JUMP_THUNK_PARAM RBM_R12
-#if NOGC_WRITE_BARRIERS
- #define REG_WRITE_BARRIER_SRC_BYREF REG_R13
- #define RBM_WRITE_BARRIER_SRC_BYREF RBM_R13
+ // ARM64 write barrier ABI (see vm\arm64\asmhelpers.asm, vm\arm64\asmhelpers.S):
+ // CORINFO_HELP_ASSIGN_REF (JIT_WriteBarrier), CORINFO_HELP_CHECKED_ASSIGN_REF (JIT_CheckedWriteBarrier):
+ // On entry:
+ // x14: the destination address (LHS of the assignment)
+ // x15: the object reference (RHS of the assignment)
+ // On exit:
+ // x12: trashed
+ // x14: incremented by 8
+ // x15: trashed
+ // x17: trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP (currently non-Windows)
+ // CORINFO_HELP_ASSIGN_BYREF (JIT_ByRefWriteBarrier):
+ // On entry:
+ // x13: the source address (points to object reference to write)
+ // x14: the destination address (object reference written here)
+ // On exit:
+ // x12: trashed
+ // x13: incremented by 8
+ // x14: incremented by 8
+ // x15: trashed
+ // x17: trashed (ip1) if FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP (currently non-Windows)
+ //
+ // Note that while x17 (ip1) is currently only trashed under FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP,
+ // currently only set for non-Windows, it is expected to be set in the future for Windows, and for R2R.
+ // So simply always consider it trashed, to avoid later breaking changes.
+
+ #define REG_WRITE_BARRIER_DST REG_R14
+ #define RBM_WRITE_BARRIER_DST RBM_R14
+
+ #define REG_WRITE_BARRIER_SRC REG_R15
+ #define RBM_WRITE_BARRIER_SRC RBM_R15
#define REG_WRITE_BARRIER_DST_BYREF REG_R14
#define RBM_WRITE_BARRIER_DST_BYREF RBM_R14
- #define REG_WRITE_BARRIER REG_R15
- #define RBM_WRITE_BARRIER RBM_R15
-#endif
+ #define REG_WRITE_BARRIER_SRC_BYREF REG_R13
+ #define RBM_WRITE_BARRIER_SRC_BYREF RBM_R13
+
+ #define RBM_CALLEE_TRASH_NOGC (RBM_R12|RBM_R15|RBM_IP1|RBM_DEFAULT_HELPER_CALL_TARGET)
+
+ // Registers killed by CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF.
+ #define RBM_CALLEE_TRASH_WRITEBARRIER (RBM_R14|RBM_CALLEE_TRASH_NOGC)
+
+ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_REF and CORINFO_HELP_CHECKED_ASSIGN_REF.
+ #define RBM_CALLEE_GCTRASH_WRITEBARRIER RBM_CALLEE_TRASH_NOGC
+
+ // Registers killed by CORINFO_HELP_ASSIGN_BYREF.
+ #define RBM_CALLEE_TRASH_WRITEBARRIER_BYREF (RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF | RBM_CALLEE_TRASH_NOGC)
+
+ // Registers no longer containing GC pointers after CORINFO_HELP_ASSIGN_BYREF.
+ // Note that x13 and x14 are still valid byref pointers after this helper call, despite their value being changed.
+ #define RBM_CALLEE_GCTRASH_WRITEBARRIER_BYREF RBM_CALLEE_TRASH_NOGC
// GenericPInvokeCalliHelper VASigCookie Parameter
#define REG_PINVOKE_COOKIE_PARAM REG_R15
#define RBM_PINVOKE_COOKIE_PARAM RBM_R15
- #define PREDICT_REG_PINVOKE_COOKIE_PARAM PREDICT_REG_R15
// GenericPInvokeCalliHelper unmanaged target Parameter
#define REG_PINVOKE_TARGET_PARAM REG_R14
#define RBM_PINVOKE_TARGET_PARAM RBM_R14
- #define PREDICT_REG_PINVOKE_TARGET_PARAM PREDICT_REG_R14
// IL stub's secret MethodDesc parameter (JitFlags::JIT_FLAG_PUBLISH_SECRET_PARAM)
#define REG_SECRET_STUB_PARAM REG_R12
@@ -1919,6 +1953,7 @@ public:
};
static const enum ArgOrder g_tgtArgOrder;
+#ifdef LEGACY_BACKEND
#if NOGC_WRITE_BARRIERS
static regMaskTP exclude_WriteBarrierReg(regMaskTP mask)
{
@@ -1929,6 +1964,7 @@ public:
return RBM_ALLINT & ~RBM_WRITE_BARRIER;
}
#endif // NOGC_WRITE_BARRIERS
+#endif // LEGACY_BACKEND
};
#if defined(DEBUG) || defined(LATE_DISASM)
diff --git a/src/vm/arm/asmhelpers.asm b/src/vm/arm/asmhelpers.asm
index b2430418d2..60def084e7 100644
--- a/src/vm/arm/asmhelpers.asm
+++ b/src/vm/arm/asmhelpers.asm
@@ -1652,7 +1652,7 @@ DoWrite
; over the lifetime of the CLR. Specifically ARM has real problems reading the values of external globals (we
; need two memory indirections to do this) so we'd like to be able to directly set the current values of
; various GC globals (e.g. g_lowest_address and g_card_table) into the barrier code itself and then reset them
-; every time they change (the GC already calls the VM to inform it of these changes). The handle this without
+; every time they change (the GC already calls the VM to inform it of these changes). To handle this without
; creating too much fragility such as hardcoding instruction offsets in the VM update code, we wrap write
; barrier creation and GC globals access in a set of macros that create a table of descriptors describing each
; offset that must be patched.
@@ -1916,10 +1916,11 @@ pShadow SETS "r7"
; is more important).
;
; Input:
- ; $ptrReg : register containing the location to be updated
- ; $valReg : register containing the value (an objref) to be written to the location above
- ; $mp : boolean indicating whether the code will run on an MP system
- ; $tmpReg : additional register that can be trashed (can alias $ptrReg or $valReg if needed)
+ ; $ptrReg : register containing the location to be updated
+ ; $valReg : register containing the value (an objref) to be written to the location above
+ ; $mp : boolean indicating whether the code will run on an MP system
+ ; $postGrow : boolean: {true} for post-grow version, {false} otherwise
+ ; $tmpReg : additional register that can be trashed (can alias $ptrReg or $valReg if needed)
;
; Output:
; $tmpReg : trashed (defaults to $ptrReg)
@@ -2005,7 +2006,7 @@ tempReg SETS "$tmpReg"
;
; Finally define the write barrier functions themselves. Currently we don't provide variations that use
; different input registers. If the JIT wants this at a later stage in order to improve code quality it would
-; be a relatively simply change to implement via an additional macro parameter to WRITE_BARRIER_ENTRY.
+; be a relatively simple change to implement via an additional macro parameter to WRITE_BARRIER_ENTRY.
;
; The calling convention for the first batch of write barriers is:
;