diff options
Diffstat (limited to 'src/jit/codegencommon.cpp')
-rw-r--r-- | src/jit/codegencommon.cpp | 265 |
1 files changed, 213 insertions, 52 deletions
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index b1e474b755..89d6a4ca34 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -107,6 +107,11 @@ CodeGen::CodeGen(Compiler* theCompiler) : CodeGenInterface(theCompiler) m_stkArgVarNum = BAD_VAR_NUM; #endif +#if defined(UNIX_X86_ABI) + curNestedAlignment = 0; + maxNestedAlignment = 0; +#endif + regTracker.rsTrackInit(compiler, ®Set); gcInfo.regSet = ®Set; m_cgEmitter = new (compiler->getAllocator()) emitter(); @@ -647,7 +652,7 @@ regMaskTP Compiler::compHelperCallKillSet(CorInfoHelpFunc helper) #if defined(_TARGET_AMD64_) return RBM_RSI | RBM_RDI | RBM_CALLEE_TRASH; #elif defined(_TARGET_ARM64_) - return RBM_CALLEE_TRASH_NOGC; + return RBM_WRITE_BARRIER_SRC_BYREF | RBM_WRITE_BARRIER_DST_BYREF | RBM_CALLEE_TRASH_NOGC; #elif defined(_TARGET_X86_) return RBM_ESI | RBM_EDI | RBM_ECX; #else @@ -717,6 +722,8 @@ regMaskTP Compiler::compNoGCHelperCallKillSet(CorInfoHelpFunc helper) #elif defined(_TARGET_X86_) // This helper only trashes ECX. return RBM_ECX; +#elif defined(_TARGET_ARM64_) + return RBM_CALLEE_TRASH_NOGC & ~(RBM_WRITE_BARRIER_SRC_BYREF | RBM_WRITE_BARRIER_DST_BYREF); #else return RBM_CALLEE_TRASH_NOGC; #endif // defined(_TARGET_AMD64_) @@ -1095,9 +1102,9 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife DEBUGARG(GenTreePtr tree) /* Can't simultaneously become live and dead at the same time */ // (deadSet UNION bornSet) != EMPTY - noway_assert(!VarSetOps::IsEmpty(this, VarSetOps::Union(this, deadSet, bornSet))); + noway_assert(!VarSetOps::IsEmptyUnion(this, deadSet, bornSet)); // (deadSet INTERSECTION bornSet) == EMPTY - noway_assert(VarSetOps::IsEmpty(this, VarSetOps::Intersection(this, deadSet, bornSet))); + noway_assert(VarSetOps::IsEmptyIntersection(this, deadSet, bornSet)); #ifdef LEGACY_BACKEND // In the LEGACY_BACKEND case, we only consider variables that are fully enregisterd @@ -1406,9 +1413,8 @@ void CodeGenInterface::reloadFloatReg(var_types type, TempDsc* tmp, regNumber re #endif // LEGACY_BACKEND // inline -regNumber CodeGenInterface::genGetThisArgReg(GenTreePtr call) +regNumber CodeGenInterface::genGetThisArgReg(GenTreeCall* call) const { - noway_assert(call->IsCall()); return REG_ARG_0; } @@ -1633,7 +1639,7 @@ void CodeGen::genDefineTempLabel(BasicBlock* label) void CodeGen::genAdjustSP(ssize_t delta) { -#ifdef _TARGET_X86_ +#if defined(_TARGET_X86_) && !defined(UNIX_X86_ABI) if (delta == sizeof(int)) inst_RV(INS_pop, REG_ECX, TYP_INT); else @@ -1663,14 +1669,14 @@ void CodeGen::genAdjustStackLevel(BasicBlock* block) { noway_assert(block->bbFlags & BBF_JMP_TARGET); - genStackLevel = compiler->fgThrowHlpBlkStkLevel(block) * sizeof(int); + SetStackLevel(compiler->fgThrowHlpBlkStkLevel(block) * sizeof(int)); if (genStackLevel != 0) { #ifdef _TARGET_X86_ getEmitter()->emitMarkStackLvl(genStackLevel); inst_RV_IV(INS_add, REG_SPBASE, genStackLevel, EA_PTRSIZE); - genStackLevel = 0; + SetStackLevel(0); #else // _TARGET_X86_ NYI("Need emitMarkStackLvl()"); #endif // _TARGET_X86_ @@ -1863,26 +1869,26 @@ bool CodeGen::genCreateAddrMode(GenTreePtr addr, The following indirections are valid address modes on x86/x64: [ icon] * not handled here - [reg ] * not handled here + [reg ] [reg + icon] - [reg2 + reg1 ] - [reg2 + reg1 + icon] - [reg2 + 2 * reg1 ] - [reg2 + 4 * reg1 ] - [reg2 + 8 * reg1 ] - [ 2 * reg1 + icon] - [ 4 * reg1 + icon] - [ 8 * reg1 + icon] - [reg2 + 2 * reg1 + icon] - [reg2 + 4 * reg1 + icon] - [reg2 + 8 * reg1 + icon] + [reg1 + reg2 ] + [reg1 + reg2 + icon] + [reg1 + 2 * reg2 ] + [reg1 + 4 * reg2 ] + [reg1 + 8 * reg2 ] + [ 2 * reg2 + icon] + [ 4 * reg2 + icon] + [ 8 * reg2 + icon] + [reg1 + 2 * reg2 + icon] + [reg1 + 4 * reg2 + icon] + [reg1 + 8 * reg2 + icon] The following indirections are valid address modes on arm64: [reg] [reg + icon] - [reg2 + reg1] - [reg2 + reg1 * natural-scale] + [reg1 + reg2] + [reg1 + reg2 * natural-scale] */ @@ -2442,6 +2448,11 @@ FOUND_AM: noway_assert(FitsIn<INT32>(cns)); + if (rv1 == nullptr && rv2 == nullptr) + { + return false; + } + /* Success - return the various components to the caller */ *revPtr = rev; @@ -2604,6 +2615,51 @@ emitJumpKind CodeGen::genJumpKindForOper(genTreeOps cmp, CompareKind compareKind return result; } +#ifndef LEGACY_BACKEND +#ifdef _TARGET_ARMARCH_ +//------------------------------------------------------------------------ +// genEmitGSCookieCheck: Generate code to check that the GS cookie +// wasn't thrashed by a buffer overrun. Coomon code for ARM32 and ARM64 +// +void CodeGen::genEmitGSCookieCheck(bool pushReg) +{ + noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal); + + // Make sure that the return register is reported as live GC-ref so that any GC that kicks in while + // executing GS cookie check will not collect the object pointed to by REG_INTRET (R0). + if (!pushReg && (compiler->info.compRetType == TYP_REF)) + gcInfo.gcRegGCrefSetCur |= RBM_INTRET; + + regNumber regGSConst = REG_TMP_0; + regNumber regGSValue = REG_TMP_1; + + if (compiler->gsGlobalSecurityCookieAddr == nullptr) + { + // load the GS cookie constant into a reg + // + genSetRegToIcon(regGSConst, compiler->gsGlobalSecurityCookieVal, TYP_I_IMPL); + } + else + { + // Ngen case - GS cookie constant needs to be accessed through an indirection. + instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + getEmitter()->emitIns_R_R_I(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regGSConst, regGSConst, 0); + } + // Load this method's GS value from the stack frame + getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regGSValue, compiler->lvaGSSecurityCookie, 0); + // Compare with the GC cookie constant + getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, regGSConst, regGSValue); + + BasicBlock* gsCheckBlk = genCreateTempLabel(); + emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED); + inst_JMP(jmpEqual, gsCheckBlk); + // regGSConst and regGSValue aren't needed anymore, we can use them for helper call + genEmitHelperCall(CORINFO_HELP_FAIL_FAST, 0, EA_UNKNOWN, regGSConst); + genDefineTempLabel(gsCheckBlk); +} +#endif // _TARGET_ARMARCH_ +#endif // !LEGACY_BACKEND + /***************************************************************************** * * Generate an exit sequence for a return from a method (note: when compiling @@ -2814,6 +2870,37 @@ void CodeGen::genUpdateCurrentFunclet(BasicBlock* block) } } } + +#if defined(_TARGET_ARM_) +void CodeGen::genInsertNopForUnwinder(BasicBlock* block) +{ + // If this block is the target of a finally return, we need to add a preceding NOP, in the same EH region, + // so the unwinder doesn't get confused by our "movw lr, xxx; movt lr, xxx; b Lyyy" calling convention that + // calls the funclet during non-exceptional control flow. + if (block->bbFlags & BBF_FINALLY_TARGET) + { + assert(block->bbFlags & BBF_JMP_TARGET); + +#ifdef DEBUG + if (compiler->verbose) + { + printf("\nEmitting finally target NOP predecessor for BB%02u\n", block->bbNum); + } +#endif + // Create a label that we'll use for computing the start of an EH region, if this block is + // at the beginning of such a region. If we used the existing bbEmitCookie as is for + // determining the EH regions, then this NOP would end up outside of the region, if this + // block starts an EH region. If we pointed the existing bbEmitCookie here, then the NOP + // would be executed, which we would prefer not to do. + + block->bbUnwindNopEmitCookie = + getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur); + + instGen(INS_nop); + } +} +#endif + #endif // FEATURE_EH_FUNCLETS /***************************************************************************** @@ -2946,7 +3033,8 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) if (compiler->fgHaveProfileData()) { - printf("; with IBC profile data\n"); + printf("; with IBC profile data, edge weights are %s, and fgCalledCount is %u\n", + compiler->fgHaveValidEdgeWeights ? "valid" : "invalid", compiler->fgCalledCount); } if (compiler->fgProfileData_ILSizeMismatch) @@ -3120,14 +3208,11 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) bool trackedStackPtrsContig; // are tracked stk-ptrs contiguous ? -#ifdef _TARGET_AMD64_ +#if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) trackedStackPtrsContig = false; #elif defined(_TARGET_ARM_) // On arm due to prespilling of arguments, tracked stk-ptrs may not be contiguous trackedStackPtrsContig = !compiler->opts.compDbgEnC && !compiler->compIsProfilerHookNeeded(); -#elif defined(_TARGET_ARM64_) - // Incoming vararg registers are homed on the top of the stack. Tracked var may not be contiguous. - trackedStackPtrsContig = !compiler->opts.compDbgEnC && !compiler->info.compIsVarArgs; #else trackedStackPtrsContig = !compiler->opts.compDbgEnC; #endif @@ -3171,7 +3256,7 @@ void CodeGen::genGenerateCode(void** codePtr, ULONG* nativeSizeOfCode) genTypeStSz(TYP_LONG) + // longs/doubles may be transferred via stack, etc (compiler->compTailCallUsed ? 4 : 0); // CORINFO_HELP_TAILCALL args #if defined(UNIX_X86_ABI) - maxAllowedStackDepth += genTypeStSz(TYP_INT) * 3; // stack align for x86 - allow up to 3 INT's for padding + maxAllowedStackDepth += maxNestedAlignment; #endif noway_assert(getEmitter()->emitMaxStackDepth <= maxAllowedStackDepth); } @@ -3896,12 +3981,12 @@ void CodeGen::genGCWriteBarrier(GenTreePtr tgt, GCInfo::WriteBarrierForm wbf) } #endif // DEBUG #endif // 0 - genStackLevel += 4; + AddStackLevel(4); inst_IV(INS_push, wbKind); genEmitHelperCall(helper, 4, // argSize EA_PTRSIZE); // retSize - genStackLevel -= 4; + SubtractStackLevel(4); } else { @@ -7520,6 +7605,7 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed) // if (compiler->fgPtrArgCntMax < 1) { + JITDUMP("Upping fgPtrArgCntMax from %d to 1\n", compiler->fgPtrArgCntMax); compiler->fgPtrArgCntMax = 1; } #elif defined(_TARGET_ARM_) @@ -7536,7 +7622,7 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed) /* Restore the stack level */ - genStackLevel = saveStackLvl2; + SetStackLevel(saveStackLvl2); #else // target NYI("Emit Profiler Enter callback"); @@ -7679,6 +7765,7 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC // if (compiler->fgPtrArgCntMax < 1) { + JITDUMP("Upping fgPtrArgCntMax from %d to 1\n", compiler->fgPtrArgCntMax); compiler->fgPtrArgCntMax = 1; } @@ -7765,7 +7852,7 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC #endif // target /* Restore the stack level */ - genStackLevel = saveStackLvl2; + SetStackLevel(saveStackLvl2); } #endif // PROFILING_SUPPORTED @@ -8054,6 +8141,14 @@ void CodeGen::genFinalizeFrame() } #endif // defined(_TARGET_ARMARCH_) +#if defined(_TARGET_ARM_) + // If there are any reserved registers, add them to the + if (regSet.rsMaskResvd != RBM_NONE) + { + regSet.rsSetRegsModified(regSet.rsMaskResvd); + } +#endif // _TARGET_ARM_ + #ifdef DEBUG if (verbose) { @@ -9239,16 +9334,23 @@ void CodeGen::genFnEpilog(BasicBlock* block) * the same descriptor with some minor adjustments. */ - getEmitter()->emitIns_Call(callType, methHnd, INDEBUG_LDISASM_COMMA(nullptr) addr, + // clang-format off + getEmitter()->emitIns_Call(callType, + methHnd, + INDEBUG_LDISASM_COMMA(nullptr) + addr, 0, // argSize EA_UNKNOWN, // retSize - gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, + gcInfo.gcVarPtrSetCur, + gcInfo.gcRegGCrefSetCur, + gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, // IL offset indCallReg, // ireg REG_NA, // xreg 0, // xmul 0, // disp true); // isJump + // clang-format on } else { @@ -9341,13 +9443,21 @@ void CodeGen::genFnEpilog(BasicBlock* block) // Simply emit a jump to the methodHnd. This is similar to a call so we can use // the same descriptor with some minor adjustments. - getEmitter()->emitIns_Call(callType, methHnd, INDEBUG_LDISASM_COMMA(nullptr) addrInfo.addr, + + // clang-format off + getEmitter()->emitIns_Call(callType, + methHnd, + INDEBUG_LDISASM_COMMA(nullptr) + addrInfo.addr, 0, // argSize EA_UNKNOWN, // retSize EA_UNKNOWN, // secondRetSize - gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, + gcInfo.gcVarPtrSetCur, + gcInfo.gcRegGCrefSetCur, + gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, 0, 0, /* iloffset, ireg, xreg, xmul, disp */ true); /* isJump */ + // clang-format on } #if FEATURE_FASTTAILCALL else @@ -9419,6 +9529,20 @@ void CodeGen::genFnEpilog(BasicBlock* block) genRestoreCalleeSavedFltRegs(compiler->compLclFrameSize); #endif // !FEATURE_STACK_FP_X87 +#ifdef JIT32_GCENCODER + // When using the JIT32 GC encoder, we do not start the OS-reported portion of the epilog until after + // the above call to `genRestoreCalleeSavedFltRegs` because that function + // a) does not actually restore any registers: there are none when targeting the Windows x86 ABI, + // which is the only target that uses the JIT32 GC encoder + // b) may issue a `vzeroupper` instruction to eliminate AVX -> SSE transition penalties. + // Because the `vzeroupper` instruction is not recognized by the VM's unwinder and there are no + // callee-save FP restores that the unwinder would need to see, we can avoid the need to change the + // unwinder (and break binary compat with older versions of the runtime) by starting the epilog + // after any `vzeroupper` instruction has been emitted. If either of the above conditions changes, + // we will need to rethink this. + getEmitter()->emitStartEpilog(); +#endif + /* Compute the size in bytes we've pushed/popped */ if (!doubleAlignOrFramePointerUsed()) @@ -9615,14 +9739,21 @@ void CodeGen::genFnEpilog(BasicBlock* block) // Simply emit a jump to the methodHnd. This is similar to a call so we can use // the same descriptor with some minor adjustments. - getEmitter()->emitIns_Call(callType, methHnd, INDEBUG_LDISASM_COMMA(nullptr) addrInfo.addr, + + // clang-format off + getEmitter()->emitIns_Call(callType, + methHnd, + INDEBUG_LDISASM_COMMA(nullptr) + addrInfo.addr, 0, // argSize EA_UNKNOWN // retSize - FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(EA_UNKNOWN), // secondRetSize + MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(EA_UNKNOWN), // secondRetSize gcInfo.gcVarPtrSetCur, - gcInfo.gcRegGCrefSetCur, gcInfo.gcRegByrefSetCur, BAD_IL_OFFSET, REG_NA, REG_NA, - 0, 0, /* iloffset, ireg, xreg, xmul, disp */ + gcInfo.gcRegGCrefSetCur, + gcInfo.gcRegByrefSetCur, + BAD_IL_OFFSET, REG_NA, REG_NA, 0, 0, /* iloffset, ireg, xreg, xmul, disp */ true); /* isJump */ + // clang-format on } #if FEATURE_FASTTAILCALL else @@ -9644,17 +9775,25 @@ void CodeGen::genFnEpilog(BasicBlock* block) unsigned stkArgSize = 0; // Zero on all platforms except x86 #if defined(_TARGET_X86_) - - noway_assert(compiler->compArgSize >= intRegState.rsCalleeRegArgCount * sizeof(void*)); - stkArgSize = compiler->compArgSize - intRegState.rsCalleeRegArgCount * sizeof(void*); - - noway_assert(compiler->compArgSize < 0x10000); // "ret" only has 2 byte operand + bool fCalleePop = true; // varargs has caller pop if (compiler->info.compIsVarArgs) - stkArgSize = 0; + fCalleePop = false; -#endif // defined(_TARGET_X86_) +#ifdef UNIX_X86_ABI + if (IsCallerPop(compiler->info.compMethodInfo->args.callConv)) + fCalleePop = false; +#endif // UNIX_X86_ABI + + if (fCalleePop) + { + noway_assert(compiler->compArgSize >= intRegState.rsCalleeRegArgCount * sizeof(void*)); + stkArgSize = compiler->compArgSize - intRegState.rsCalleeRegArgCount * sizeof(void*); + + noway_assert(compiler->compArgSize < 0x10000); // "ret" only has 2 byte operand + } +#endif // _TARGET_X86_ /* Return, popping our arguments (if any) */ instGen_Return(stkArgSize); @@ -10271,6 +10410,22 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo() /***************************************************************************** * * Generates code for an EH funclet prolog. + * + * + * Funclets have the following incoming arguments: + * + * catch/filter-handler: eax = the exception object that was caught (see GT_CATCH_ARG) + * filter: eax = the exception object that was caught (see GT_CATCH_ARG) + * finally/fault: none + * + * Funclets set the following registers on exit: + * + * catch/filter-handler: eax = the address at which execution should resume (see BBJ_EHCATCHRET) + * filter: eax = non-zero if the handler should handle the exception, zero otherwise (see GT_RETFILT) + * finally/fault: none + * + * Funclet prolog/epilog sequence and funclet frame layout are TBD. + * */ void CodeGen::genFuncletProlog(BasicBlock* block) @@ -10284,12 +10439,17 @@ void CodeGen::genFuncletProlog(BasicBlock* block) ScopedSetVariable<bool> _setGeneratingProlog(&compiler->compGeneratingProlog, true); - compiler->unwindBegProlog(); + gcInfo.gcResetForBB(); - // TODO Save callee-saved registers + compiler->unwindBegProlog(); // This is the end of the OS-reported prolog for purposes of unwinding compiler->unwindEndProlog(); + + // TODO We may need EBP restore sequence here if we introduce PSPSym + + // Add a padding for 16-byte alignment + inst_RV_IV(INS_sub, REG_SPBASE, 12, EA_PTRSIZE); } /***************************************************************************** @@ -10308,7 +10468,8 @@ void CodeGen::genFuncletEpilog() ScopedSetVariable<bool> _setGeneratingEpilog(&compiler->compGeneratingEpilog, true); - // TODO Restore callee-saved registers + // Revert a padding that was added for 16-byte alignment + inst_RV_IV(INS_add, REG_SPBASE, 12, EA_PTRSIZE); instGen_Return(0); } @@ -11061,7 +11222,7 @@ unsigned CodeGen::getFirstArgWithStackSlot() // void CodeGen::genSinglePush() { - genStackLevel += sizeof(void*); + AddStackLevel(REGSIZE_BYTES); } //------------------------------------------------------------------------ @@ -11069,7 +11230,7 @@ void CodeGen::genSinglePush() // void CodeGen::genSinglePop() { - genStackLevel -= sizeof(void*); + SubtractStackLevel(REGSIZE_BYTES); } //------------------------------------------------------------------------ |