diff --git a/src/coreclr/CMakeLists.txt b/src/coreclr/CMakeLists.txt index a85241b17d3085..403175ba308f59 100644 --- a/src/coreclr/CMakeLists.txt +++ b/src/coreclr/CMakeLists.txt @@ -203,7 +203,9 @@ include_directories("${GENERATED_INCLUDE_DIR}") include_directories("hosts/inc") include_directories("minipal") +if(FEATURE_INTERPRETER) add_subdirectory(interpreter) +endif() if(CLR_CMAKE_TARGET_WIN32 AND FEATURE_EVENT_TRACE) include_directories("${GENERATED_INCLUDE_DIR}/etw") diff --git a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt index 4b7d533bb19ea5..f626cbed7db3ae 100644 --- a/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt +++ b/src/coreclr/dlls/mscoree/coreclr/CMakeLists.txt @@ -98,7 +98,6 @@ set(CORECLR_LIBRARIES ceefgen comfloat_wks corguids - gcinfo utilcode v3binder System.Globalization.Native-Static diff --git a/src/coreclr/gcinfo/arraylist.cpp b/src/coreclr/gcinfo/arraylist.cpp index 5071c483ba7ff3..84267b00e22595 100644 --- a/src/coreclr/gcinfo/arraylist.cpp +++ b/src/coreclr/gcinfo/arraylist.cpp @@ -1,9 +1,23 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +// Interpreter-FIXME: we get an existing implementation of ASSERTE via PCH that isn't usable +// from inside the interpreter, so we need to replace it with our own. +#undef _ASSERTE + +#if defined(_DEBUG) + +extern "C" void assertAbort(const char* why, const char* file, unsigned line); + +#define _ASSERTE(expr) if (!(expr)) { \ + assertAbort(#expr, __FILE__, __LINE__); \ +} +#else // _DEBUG +#define _ASSERTE(expr) (void)0 +#endif // _DEBUG + +#include "gcinfohelpers.h" #include -#include -#include "debugmacros.h" #include "iallocator.h" #include "gcinfoarraylist.h" #include "safemath.h" @@ -11,8 +25,8 @@ inline size_t roundUp(size_t size, size_t alignment) { // `alignment` must be a power of two - assert(alignment != 0); - assert((alignment & (alignment - 1)) == 0); + _ASSERTE(alignment != 0); + _ASSERTE((alignment & (alignment - 1)) == 0); return (size + (alignment - 1)) & ~(alignment - 1); } @@ -25,7 +39,7 @@ GcInfoArrayListBase::GcInfoArrayListBase(IAllocator* allocator) m_lastChunkCapacity(0), m_itemCount(0) { - assert(m_allocator != nullptr); + _ASSERTE(m_allocator != nullptr); } GcInfoArrayListBase::~GcInfoArrayListBase() @@ -42,19 +56,19 @@ void GcInfoArrayListBase::AppendNewChunk(size_t firstChunkCapacity, size_t eleme size_t chunkCapacity = (m_firstChunk == nullptr) ? firstChunkCapacity : (m_lastChunkCapacity * GrowthFactor); S_SIZE_T chunkSize = S_SIZE_T(roundUp(sizeof(ChunkBase), chunkAlignment)) + (S_SIZE_T(elementSize) * S_SIZE_T(chunkCapacity)); - assert(!chunkSize.IsOverflow()); + _ASSERTE(!chunkSize.IsOverflow()); ChunkBase* chunk = reinterpret_cast(m_allocator->Alloc(chunkSize.Value())); chunk->m_next = nullptr; if (m_lastChunk != nullptr) { - assert(m_firstChunk != nullptr); + _ASSERTE(m_firstChunk != nullptr); m_lastChunk->m_next = chunk; } else { - assert(m_lastChunk == nullptr); + _ASSERTE(m_lastChunk == nullptr); m_firstChunk = chunk; } @@ -66,7 +80,7 @@ void GcInfoArrayListBase::AppendNewChunk(size_t firstChunkCapacity, size_t eleme GcInfoArrayListBase::IteratorBase::IteratorBase(GcInfoArrayListBase* list, size_t firstChunkCapacity) : m_list(list) { - assert(m_list != nullptr); + _ASSERTE(m_list != nullptr); // Note: if the list is empty, m_list->firstChunk == nullptr == m_list->lastChunk and m_lastChunkCount == 0. // In that case, the next two lines will set m_currentChunk to nullptr and m_currentChunkCount to 0. diff --git a/src/coreclr/gcinfo/gcinfodumper.cpp b/src/coreclr/gcinfo/gcinfodumper.cpp index 1c8c79c10c880d..c22850c2b0f10c 100644 --- a/src/coreclr/gcinfo/gcinfodumper.cpp +++ b/src/coreclr/gcinfo/gcinfodumper.cpp @@ -4,6 +4,8 @@ #ifndef SOS_INCLUDE #include "common.h" #endif + +#include "gcinfohelpers.h" #include "gcinfodumper.h" #include "gcinfodecoder.h" diff --git a/src/coreclr/gcinfo/gcinfoencoder.cpp b/src/coreclr/gcinfo/gcinfoencoder.cpp index 959d42d91bc387..4654e2e28b6d70 100644 --- a/src/coreclr/gcinfo/gcinfoencoder.cpp +++ b/src/coreclr/gcinfo/gcinfoencoder.cpp @@ -7,8 +7,25 @@ * */ +// Interpreter-FIXME: we get an existing implementation of ASSERTE via PCH that isn't usable +// from inside the interpreter, so we need to replace it with our own. +#undef _ASSERTE + +#if defined(_DEBUG) + +extern "C" void assertAbort(const char* why, const char* file, unsigned line); + +#define _ASSERTE(expr) if (!(expr)) { \ + assertAbort(#expr, __FILE__, __LINE__); \ +} +#else // _DEBUG +#define _ASSERTE(expr) (void)0 +#endif // _DEBUG + + #include +#include "gcinfohelpers.h" #include "gcinfoencoder.h" using namespace GcInfoEncoderExt; @@ -22,7 +39,6 @@ using namespace GcInfoEncoderExt; #endif #ifndef STANDALONE_BUILD -#include "log.h" #include "simplerhash.h" #include "bitposition.h" #endif @@ -381,49 +397,48 @@ GcInfoSize& GcInfoSize::operator+=(const GcInfoSize& other) void GcInfoSize::Log(DWORD level, const char * header) { - if(LoggingOn(LF_GCINFO, level)) - { - LogSpew(LF_GCINFO, level, header); - - LogSpew(LF_GCINFO, level, "---COUNTS---\n"); - LogSpew(LF_GCINFO, level, "NumMethods: %zu\n", NumMethods); - LogSpew(LF_GCINFO, level, "NumCallSites: %zu\n", NumCallSites); - LogSpew(LF_GCINFO, level, "NumRanges: %zu\n", NumRanges); - LogSpew(LF_GCINFO, level, "NumRegs: %zu\n", NumRegs); - LogSpew(LF_GCINFO, level, "NumStack: %zu\n", NumStack); - LogSpew(LF_GCINFO, level, "NumUntracked: %zu\n", NumUntracked); - LogSpew(LF_GCINFO, level, "NumTransitions: %zu\n", NumTransitions); - LogSpew(LF_GCINFO, level, "SizeOfCode: %zu\n", SizeOfCode); - LogSpew(LF_GCINFO, level, "EncInfoSize: %zu\n", EncInfoSize); - - LogSpew(LF_GCINFO, level, "---SIZES(bits)---\n"); - LogSpew(LF_GCINFO, level, "Total: %zu\n", TotalSize); - LogSpew(LF_GCINFO, level, "UntrackedSlot: %zu\n", UntrackedSlotSize); - LogSpew(LF_GCINFO, level, "NumUntracked: %zu\n", NumUntrackedSize); - LogSpew(LF_GCINFO, level, "Flags: %zu\n", FlagsSize); - LogSpew(LF_GCINFO, level, "CodeLength: %zu\n", CodeLengthSize); - LogSpew(LF_GCINFO, level, "Prolog/Epilog: %zu\n", ProEpilogSize); - LogSpew(LF_GCINFO, level, "SecObj: %zu\n", SecObjSize); - LogSpew(LF_GCINFO, level, "GsCookie: %zu\n", GsCookieSize); - LogSpew(LF_GCINFO, level, "GenericsCtx: %zu\n", GenericsCtxSize); - LogSpew(LF_GCINFO, level, "StackBase: %zu\n", StackBaseSize); - LogSpew(LF_GCINFO, level, "FixedArea: %zu\n", FixedAreaSize); - LogSpew(LF_GCINFO, level, "ReversePInvokeFrame: %zu\n", ReversePInvokeFrameSize); - LogSpew(LF_GCINFO, level, "NumCallSites: %zu\n", NumCallSitesSize); - LogSpew(LF_GCINFO, level, "NumRanges: %zu\n", NumRangesSize); - LogSpew(LF_GCINFO, level, "CallSiteOffsets: %zu\n", CallSitePosSize); - LogSpew(LF_GCINFO, level, "Ranges: %zu\n", RangeSize); - LogSpew(LF_GCINFO, level, "NumRegs: %zu\n", NumRegsSize); - LogSpew(LF_GCINFO, level, "NumStack: %zu\n", NumStackSize); - LogSpew(LF_GCINFO, level, "RegSlots: %zu\n", RegSlotSize); - LogSpew(LF_GCINFO, level, "StackSlots: %zu\n", StackSlotSize); - LogSpew(LF_GCINFO, level, "CallSiteStates: %zu\n", CallSiteStateSize); - LogSpew(LF_GCINFO, level, "EhOffsets: %zu\n", EhPosSize); - LogSpew(LF_GCINFO, level, "EhStates: %zu\n", EhStateSize); - LogSpew(LF_GCINFO, level, "ChunkPointers: %zu\n", ChunkPtrSize); - LogSpew(LF_GCINFO, level, "ChunkMasks: %zu\n", ChunkMaskSize); - LogSpew(LF_GCINFO, level, "ChunkFinalStates: %zu\n", ChunkFinalStateSize); - LogSpew(LF_GCINFO, level, "Transitions: %zu\n", ChunkTransitionSize); + if (GCINFO_LOGSPEW(level, header)) + { + GCINFO_LOGSPEW( level, "---COUNTS---\n"); + GCINFO_LOGSPEW( level, "NumMethods: %zu\n", NumMethods); + GCINFO_LOGSPEW( level, "NumCallSites: %zu\n", NumCallSites); + GCINFO_LOGSPEW( level, "NumRanges: %zu\n", NumRanges); + GCINFO_LOGSPEW( level, "NumRegs: %zu\n", NumRegs); + GCINFO_LOGSPEW( level, "NumStack: %zu\n", NumStack); + GCINFO_LOGSPEW( level, "NumUntracked: %zu\n", NumUntracked); + GCINFO_LOGSPEW( level, "NumTransitions: %zu\n", NumTransitions); + GCINFO_LOGSPEW( level, "SizeOfCode: %zu\n", SizeOfCode); + GCINFO_LOGSPEW( level, "EncInfoSize: %zu\n", EncInfoSize); + + GCINFO_LOGSPEW( level, "---SIZES(bits)---\n"); + GCINFO_LOGSPEW( level, "Total: %zu\n", TotalSize); + GCINFO_LOGSPEW( level, "UntrackedSlot: %zu\n", UntrackedSlotSize); + GCINFO_LOGSPEW( level, "NumUntracked: %zu\n", NumUntrackedSize); + GCINFO_LOGSPEW( level, "Flags: %zu\n", FlagsSize); + GCINFO_LOGSPEW( level, "CodeLength: %zu\n", CodeLengthSize); + GCINFO_LOGSPEW( level, "Prolog/Epilog: %zu\n", ProEpilogSize); + GCINFO_LOGSPEW( level, "SecObj: %zu\n", SecObjSize); + GCINFO_LOGSPEW( level, "GsCookie: %zu\n", GsCookieSize); + GCINFO_LOGSPEW( level, "PspSym: %zu\n", PspSymSize); + GCINFO_LOGSPEW( level, "GenericsCtx: %zu\n", GenericsCtxSize); + GCINFO_LOGSPEW( level, "StackBase: %zu\n", StackBaseSize); + GCINFO_LOGSPEW( level, "FixedArea: %zu\n", FixedAreaSize); + GCINFO_LOGSPEW( level, "ReversePInvokeFrame: %zu\n", ReversePInvokeFrameSize); + GCINFO_LOGSPEW( level, "NumCallSites: %zu\n", NumCallSitesSize); + GCINFO_LOGSPEW( level, "NumRanges: %zu\n", NumRangesSize); + GCINFO_LOGSPEW( level, "CallSiteOffsets: %zu\n", CallSitePosSize); + GCINFO_LOGSPEW( level, "Ranges: %zu\n", RangeSize); + GCINFO_LOGSPEW( level, "NumRegs: %zu\n", NumRegsSize); + GCINFO_LOGSPEW( level, "NumStack: %zu\n", NumStackSize); + GCINFO_LOGSPEW( level, "RegSlots: %zu\n", RegSlotSize); + GCINFO_LOGSPEW( level, "StackSlots: %zu\n", StackSlotSize); + GCINFO_LOGSPEW( level, "CallSiteStates: %zu\n", CallSiteStateSize); + GCINFO_LOGSPEW( level, "EhOffsets: %zu\n", EhPosSize); + GCINFO_LOGSPEW( level, "EhStates: %zu\n", EhStateSize); + GCINFO_LOGSPEW( level, "ChunkPointers: %zu\n", ChunkPtrSize); + GCINFO_LOGSPEW( level, "ChunkMasks: %zu\n", ChunkMaskSize); + GCINFO_LOGSPEW( level, "ChunkFinalStates: %zu\n", ChunkFinalStateSize); + GCINFO_LOGSPEW( level, "Transitions: %zu\n", ChunkTransitionSize); } } @@ -440,11 +455,6 @@ template TGcInfoEncoder::TGcInfoEncode m_InterruptibleRanges( pJitAllocator ), m_LifetimeTransitions( pJitAllocator ) { -#ifdef MEASURE_GCINFO - // This causes multiple complus.log files in JIT64. TODO: consider using ICorJitInfo::logMsg instead. - InitializeLogging(); -#endif - _ASSERTE( pCorJitInfo != NULL ); _ASSERTE( pMethodInfo != NULL ); _ASSERTE( pJitAllocator != NULL ); @@ -636,7 +646,7 @@ template void TGcInfoEncoder::DefineIn } } - LOG((LF_GCINFO, LL_INFO1000000, "interruptible at %x length %x\n", startInstructionOffset, length)); + GCINFO_LOG( LL_INFO1000000, "interruptible at %x length %x\n", startInstructionOffset, length); } @@ -661,7 +671,7 @@ template void TGcInfoEncoder::SetSlotS *( m_LifetimeTransitions.Append() ) = transition; - LOG((LF_GCINFO, LL_INFO1000000, LOG_GCSLOTDESC_FMT " %s at %x\n", LOG_GCSLOTDESC_ARGS(&m_SlotTable[slotId]), slotState == GC_SLOT_LIVE ? "live" : "dead", instructionOffset)); + GCINFO_LOG( LL_INFO1000000, LOG_GCSLOTDESC_FMT " %s at %x\n", LOG_GCSLOTDESC_ARGS(&m_SlotTable[slotId]), slotState == GC_SLOT_LIVE ? "live" : "dead", instructionOffset); } @@ -761,6 +771,7 @@ template void TGcInfoEncoder::SetRever m_ReversePInvokeFrameSlot = spOffset; } + struct GcSlotDescAndId { GcSlotDesc m_SlotDesc; @@ -834,7 +845,7 @@ struct CompareLifetimeTransitionsBySlot UINT32 firstOffset = first.CodeOffset; UINT32 secondOffset = second.CodeOffset; - // FIXME: GcInfoEncoding:: + // Interpreter-FIXME: GcInfoEncoding:: // _ASSERTE(GetNormCodeOffsetChunk(firstOffset) == GetNormCodeOffsetChunk(secondOffset)); // Sort them by slot @@ -895,6 +906,7 @@ void BitStreamWriter::MemoryBlockList::Dispose(IAllocator* allocator) #endif } + template void TGcInfoEncoder::FinalizeSlotIds() { #ifdef _DEBUG @@ -915,9 +927,9 @@ template void TGcInfoEncoder::Build() char className[256]; m_pCorJitInfo->printClassName(m_pCorJitInfo->getMethodClass(m_pMethodInfo->ftn), className, sizeof(className)); - LOG((LF_GCINFO, LL_INFO100, + GCINFO_LOG( LL_INFO100, "Entering GcInfoEncoder::Build() for method %s:%s\n", - className, methodName)); + className, methodName); #endif @@ -1017,9 +1029,9 @@ template void TGcInfoEncoder::Build() { _ASSERTE(!slimHeader); #ifdef _DEBUG - LOG((LF_GCINFO, LL_INFO1000, "GS cookie at " FMT_STK "\n", + GCINFO_LOG( LL_INFO1000, "GS cookie at " FMT_STK "\n", DBG_STK(m_GSCookieStackSlot) - )); + ); #endif GCINFO_WRITE_VARL_S(m_Info1, GcInfoEncoding::NORMALIZE_STACK_SLOT(m_GSCookieStackSlot), GcInfoEncoding::GS_COOKIE_STACK_SLOT_ENCBASE, GsCookieSize); @@ -1031,9 +1043,9 @@ template void TGcInfoEncoder::Build() { _ASSERTE(!slimHeader); #ifdef _DEBUG - LOG((LF_GCINFO, LL_INFO1000, "Generics instantiation context at " FMT_STK "\n", + GCINFO_LOG( LL_INFO1000, "Generics instantiation context at " FMT_STK "\n", DBG_STK(m_GenericsInstContextStackSlot) - )); + ); #endif GCINFO_WRITE_VARL_S(m_Info1, GcInfoEncoding::NORMALIZE_STACK_SLOT(m_GenericsInstContextStackSlot), GcInfoEncoding::GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE, GenericsCtxSize); } @@ -1943,7 +1955,7 @@ template void TGcInfoEncoder::Build() // Encode transitions //------------------------------------------------------------------ - LOG((LF_GCINFO, LL_INFO1000, "Encoding %i lifetime transitions.\n", pEndTransitions - pTransitions)); + GCINFO_LOG( LL_INFO1000, "Encoding %i lifetime transitions.\n", pEndTransitions - pTransitions); liveState.ClearAll(); @@ -1992,11 +2004,11 @@ template void TGcInfoEncoder::Build() // Write couldBeLive slot map GCINFO_WRITE_VAR_VECTOR(m_Info2, couldBeLive, GcInfoEncoding::LIVESTATE_RLE_SKIP_ENCBASE, GcInfoEncoding::LIVESTATE_RLE_RUN_ENCBASE, ChunkMaskSize); - LOG((LF_GCINFO, LL_INFO100000, + GCINFO_LOG( LL_INFO100000, "Chunk %d couldBeLive (%04x-%04x):\n", currentChunk, currentChunk * GcInfoEncoding::NUM_NORM_CODE_OFFSETS_PER_CHUNK, ((currentChunk + 1) * GcInfoEncoding::NUM_NORM_CODE_OFFSETS_PER_CHUNK) - 1 - )); + ); // Write final state // For all the bits set in couldBeLive. @@ -2013,10 +2025,10 @@ template void TGcInfoEncoder::Build() ChunkFinalStateSize ); - LOG((LF_GCINFO, LL_INFO100000, + GCINFO_LOG( LL_INFO100000, "\t" LOG_GCSLOTDESC_FMT " %s at end of chunk.\n", LOG_GCSLOTDESC_ARGS(&m_SlotTable[i]), - liveState.ReadBit(i) ? "live" : "dead")); + liveState.ReadBit(i) ? "live" : "dead"); } } @@ -2037,11 +2049,11 @@ template void TGcInfoEncoder::Build() _ASSERTE(couldBeLive.ReadBit(slotId)); - LOG((LF_GCINFO, LL_INFO100000, + GCINFO_LOG( LL_INFO100000, "\tTransition " LOG_GCSLOTDESC_FMT " going %s at offset %04x.\n", LOG_GCSLOTDESC_ARGS(&m_SlotTable[pT->SlotId]), pT->BecomesLive ? "live" : "dead", - (int) pT->CodeOffset )); + (int) pT->CodeOffset ); // Write code offset delta UINT32 normCodeOffset = pT->CodeOffset; @@ -2150,8 +2162,8 @@ lExitSuccess:; m_CurrentMethodSize.Log(LL_INFO100, "=== PartiallyInterruptible method breakdown ===\r\n"); g_PiGcInfoSize.Log(LL_INFO10, "=== PartiallyInterruptible global breakdown ===\r\n"); } - LogSpew(LF_GCINFO, LL_INFO10, "Total SlimHeaders: %zu\n", g_NumSlimHeaders); - LogSpew(LF_GCINFO, LL_INFO10, "NumMethods: %zu\n", g_NumFatHeaders); + GCINFO_LOGSPEW( LL_INFO10, "Total SlimHeaders: %zu\n", g_NumSlimHeaders); + GCINFO_LOGSPEW( LL_INFO10, "NumMethods: %zu\n", g_NumFatHeaders); #endif } @@ -2397,7 +2409,7 @@ template BYTE* TGcInfoEncoder::Emit() size_t cbGcInfoSize = m_Info1.GetByteCount() + m_Info2.GetByteCount(); - LOG((LF_GCINFO, LL_INFO100, "GcInfoEncoder::Emit(): Size of GC info is %u bytes, code size %u bytes.\n", (unsigned)cbGcInfoSize, m_CodeLength )); + GCINFO_LOG( LL_INFO100, "GcInfoEncoder::Emit(): Size of GC info is %u bytes, code size %u bytes.\n", (unsigned)cbGcInfoSize, m_CodeLength ); BYTE* destBuffer = (BYTE *)eeAllocGCInfo(cbGcInfoSize); // Allocator will throw an exception on failure. @@ -2432,6 +2444,7 @@ template size_t TGcInfoEncoder::GetEnc return m_BlockSize; } + BitStreamWriter::BitStreamWriter( IAllocator* pAllocator ) { m_pAllocator = pAllocator; @@ -2451,7 +2464,7 @@ BitStreamWriter::BitStreamWriter( IAllocator* pAllocator ) // void BitStreamWriter::Write( size_t data, UINT32 count ) { - _ASSERT(count <= BITS_PER_SIZE_T); + _ASSERTE(count <= BITS_PER_SIZE_T); if(count) { @@ -2526,6 +2539,21 @@ void BitStreamWriter::CopyTo( BYTE* buffer ) } +inline void BitStreamWriter::AllocMemoryBlock() +{ + // Interpreter-FIXME: Causes linker error in interpreter because IS_ALIGNED calls _ASSERTE + // _ASSERTE( IS_ALIGNED( m_MemoryBlockSize, sizeof( size_t ) ) ); + MemoryBlock* pMemBlock = m_MemoryBlocks.AppendNew(m_pAllocator, m_MemoryBlockSize); + + m_pCurrentSlot = pMemBlock->Contents; + m_OutOfBlockSlot = m_pCurrentSlot + m_MemoryBlockSize / sizeof( size_t ); + +#ifdef _DEBUG + m_MemoryBlocksCount++; +#endif +} + + void BitStreamWriter::Dispose() { m_MemoryBlocks.Dispose(m_pAllocator); @@ -2600,3 +2628,7 @@ int BitStreamWriter::EncodeVarLengthSigned( SSIZE_T n, UINT32 base ) // Instantiate the encoder so other files can use it template class TGcInfoEncoder; + +#ifdef FEATURE_INTERPRETER +template class TGcInfoEncoder; +#endif // FEATURE_INTERPRETER diff --git a/src/coreclr/gcinfo/gcinfohelpers.h b/src/coreclr/gcinfo/gcinfohelpers.h new file mode 100644 index 00000000000000..da296817d2798a --- /dev/null +++ b/src/coreclr/gcinfo/gcinfohelpers.h @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#ifndef _GCINFOHELPERS_H_ +#define _GCINFOHELPERS_H_ + +// If you want GcInfoEncoder logging to work, replace this macro with an appropriate definition. +// This previously relied on our common logging infrastructure, but that caused linker failures in the interpreter. +// Example implementation: +// #define GCINFO_LOG(level, format, ...) (printf(format, __VA_ARGS__), true) +#define GCINFO_LOG(level, format, ...) false + +// If you want to enable GcInfoSize::Log to work, replace this macro with an appropriate definition. +#define GCINFO_LOGSPEW(level, format, ...) false + +// Duplicated from log.h +// NOTE: ICorJitInfo::logMsg appears to accept these same levels and is accessible from GcInfoEncoder. +#define LL_EVERYTHING 10 +#define LL_INFO1000000 9 // can be expected to generate 1,000,000 logs per small but not trivial run +#define LL_INFO100000 8 // can be expected to generate 100,000 logs per small but not trivial run +#define LL_INFO10000 7 // can be expected to generate 10,000 logs per small but not trivial run +#define LL_INFO1000 6 // can be expected to generate 1,000 logs per small but not trivial run +#define LL_INFO100 5 // can be expected to generate 100 logs per small but not trivial run +#define LL_INFO10 4 // can be expected to generate 10 logs per small but not trivial run +#define LL_WARNING 3 +#define LL_ERROR 2 +#define LL_FATALERROR 1 +#define LL_ALWAYS 0 // impossible to turn off (log level never negative) + +#endif // _GCINFOHELPERS_H_ diff --git a/src/coreclr/inc/debugmacros.h b/src/coreclr/inc/debugmacros.h index d5ed757ae77aab..3d2918f683fd86 100644 --- a/src/coreclr/inc/debugmacros.h +++ b/src/coreclr/inc/debugmacros.h @@ -15,7 +15,6 @@ #include "palclr.h" #include -#undef _ASSERTE #undef VERIFY #ifdef __cplusplus @@ -56,8 +55,12 @@ extern VOID ANALYZER_NORETURN DbgAssertDialog(const char *szFile, int iLine, con #else // !_DEBUG -#define _ASSERTE(expr) ((void)0) -#define _ASSERTE_MSG(expr, msg) ((void)0) +#if !defined(_ASSERTE) + #define _ASSERTE(expr) ((void)0) +#endif +#if !defined(_ASSERTE_MSG) + #define _ASSERTE_MSG(expr, msg) ((void)0) +#endif #define VERIFY(stmt) (void)(stmt) // At this point, EEPOLICY_HANDLE_FATAL_ERROR may or may not be defined. It will be defined diff --git a/src/coreclr/inc/gcinfodecoder.h b/src/coreclr/inc/gcinfodecoder.h index 49ca335b69e2b3..87693d89d25c9f 100644 --- a/src/coreclr/inc/gcinfodecoder.h +++ b/src/coreclr/inc/gcinfodecoder.h @@ -781,6 +781,9 @@ class TGcInfoDecoder }; typedef TGcInfoDecoder GcInfoDecoder; +#ifdef FEATURE_INTERPRETER +typedef TGcInfoDecoder InterpreterGcInfoDecoder; +#endif // FEATURE_INTERPRETER #endif // USE_GC_INFO_DECODER diff --git a/src/coreclr/inc/gcinfoencoder.h b/src/coreclr/inc/gcinfoencoder.h index ce07197a8447bc..273d142a3435aa 100644 --- a/src/coreclr/inc/gcinfoencoder.h +++ b/src/coreclr/inc/gcinfoencoder.h @@ -289,19 +289,7 @@ class BitStreamWriter *m_pCurrentSlot |= data; } - inline void AllocMemoryBlock() - { - _ASSERTE( IS_ALIGNED( m_MemoryBlockSize, sizeof( size_t ) ) ); - MemoryBlock* pMemBlock = m_MemoryBlocks.AppendNew(m_pAllocator, m_MemoryBlockSize); - - m_pCurrentSlot = pMemBlock->Contents; - m_OutOfBlockSlot = m_pCurrentSlot + m_MemoryBlockSize / sizeof( size_t ); - -#ifdef _DEBUG - m_MemoryBlocksCount++; -#endif - - } + inline void AllocMemoryBlock(); inline void InitCurrentSlot() { @@ -558,4 +546,8 @@ class TGcInfoEncoder typedef TGcInfoEncoder GcInfoEncoder; +#ifdef FEATURE_INTERPRETER +typedef TGcInfoEncoder InterpreterGcInfoEncoder; +#endif // FEATURE_INTERPRETER + #endif // !__GCINFOENCODER_H__ diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index 5756902f8fa4ec..d83fe0a0086666 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -965,6 +965,62 @@ struct X86GcInfoEncoding { #endif // defined(TARGET_xxx) +#ifdef FEATURE_INTERPRETER + +struct InterpreterGcInfoEncoding { + static const uint32_t NUM_NORM_CODE_OFFSETS_PER_CHUNK = (64); + + static const uint32_t NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2 = (6); + // Interpreter-FIXME: Interpreter has fixed-size stack slots so we could normalize them based on that. + static inline constexpr int32_t NORMALIZE_STACK_SLOT (int32_t x) { return (x); } + static inline constexpr int32_t DENORMALIZE_STACK_SLOT (int32_t x) { return (x); } + // Interpreter-FIXME: Interpreter has fixed-size opcodes so code length is a multiple of that. + static inline constexpr uint32_t NORMALIZE_CODE_LENGTH (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_CODE_LENGTH (uint32_t x) { return (x); } + + static inline constexpr uint32_t NORMALIZE_STACK_BASE_REGISTER (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_STACK_BASE_REGISTER (uint32_t x) { return (x); } + // Interpreter-FIXME: Interpreter has fixed-size stack slots so we could normalize them based on that. + static inline constexpr uint32_t NORMALIZE_SIZE_OF_STACK_AREA (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_SIZE_OF_STACK_AREA (uint32_t x) { return (x); } + static const bool CODE_OFFSETS_NEED_NORMALIZATION = false; + // Interpreter-FIXME: Interpreter has fixed-size opcodes so code length is a multiple of that. + static inline constexpr uint32_t NORMALIZE_CODE_OFFSET (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_CODE_OFFSET (uint32_t x) { return (x); } + + static const int PSP_SYM_STACK_SLOT_ENCBASE = 6; + static const int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE = 6; + static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; + static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; + static const int CODE_LENGTH_ENCBASE = 8; + static const int STACK_BASE_REGISTER_ENCBASE = 3; + static const int SIZE_OF_STACK_AREA_ENCBASE = 3; + static const int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 4; + // Interpreter-FIXME: This constant is only used on certain architectures. + static const int SIZE_OF_EDIT_AND_CONTINUE_FIXED_STACK_FRAME_ENCBASE = 4; + static const int REVERSE_PINVOKE_FRAME_ENCBASE = 6; + static const int NUM_REGISTERS_ENCBASE = 2; + static const int NUM_STACK_SLOTS_ENCBASE = 2; + static const int NUM_UNTRACKED_SLOTS_ENCBASE = 1; + static const int NORM_PROLOG_SIZE_ENCBASE = 5; + static const int NORM_EPILOG_SIZE_ENCBASE = 3; + static const int NORM_CODE_OFFSET_DELTA_ENCBASE = 3; + static const int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 6; + static const int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE = 6; + static const int REGISTER_ENCBASE = 3; + static const int REGISTER_DELTA_ENCBASE = 2; + static const int STACK_SLOT_ENCBASE = 6; + static const int STACK_SLOT_DELTA_ENCBASE = 4; + static const int NUM_SAFE_POINTS_ENCBASE = 2; + static const int NUM_INTERRUPTIBLE_RANGES_ENCBASE = 1; + static const int NUM_EH_CLAUSES_ENCBASE = 2; + static const int POINTER_SIZE_ENCBASE = 3; + static const int LIVESTATE_RLE_RUN_ENCBASE = 2; + static const int LIVESTATE_RLE_SKIP_ENCBASE = 4; +}; + +#endif // FEATURE_INTERPRETER + #ifdef debug_instrumented_return #define return debug_instrumented_return #endif // debug_instrumented_return diff --git a/src/coreclr/interpreter/CMakeLists.txt b/src/coreclr/interpreter/CMakeLists.txt index fc1feeb64048b4..8962c612a71cde 100644 --- a/src/coreclr/interpreter/CMakeLists.txt +++ b/src/coreclr/interpreter/CMakeLists.txt @@ -7,6 +7,7 @@ set(INTERPRETER_SOURCES eeinterp.cpp) set(INTERPRETER_LINK_LIBRARIES + gcinfo ) if(CLR_CMAKE_HOST_WIN32) diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 6a073b105cffb2..546d9a0a2d9835 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -1,9 +1,18 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#include "gcinfoencoder.h" + +// HACK: debugreturn.h (included by gcinfoencoder.h) breaks constexpr +#if defined(debug_instrumented_return) || defined(_DEBUGRETURN_H_) +#undef return +#endif // debug_instrumented_return + #include "interpreter.h" #include +#include // for std::bad_alloc + static const StackType g_stackTypeFromInterpType[] = { StackTypeI4, // I1 @@ -31,9 +40,55 @@ static const InterpType g_interpTypeFromStackType[] = InterpTypeI, // F }; +// Used by assertAbort +thread_local ICorJitInfo* t_InterpJitInfoTls = nullptr; + static const char *g_stackTypeString[] = { "I4", "I8", "R4", "R8", "O ", "VT", "MP", "F " }; -// FIXME Use specific allocators for their intended purpose +/*****************************************************************************/ +void DECLSPEC_NORETURN Interp_NOMEM() +{ + throw std::bad_alloc(); +} + +// GCInfoEncoder needs an IAllocator implementation. This is a simple one that forwards to the Compiler. +class InterpIAllocator : public IAllocator +{ + InterpCompiler *m_pCompiler; + +public: + InterpIAllocator(InterpCompiler *compiler) + : m_pCompiler(compiler) + { + } + + // Allocates a block of memory at least `sz` in size. + virtual void* Alloc(size_t sz) override + { + return m_pCompiler->AllocMethodData(sz); + } + + // Allocates a block of memory at least `elems * elemSize` in size. + virtual void* ArrayAlloc(size_t elems, size_t elemSize) override + { + // Ensure that elems * elemSize does not overflow. + if (elems > (SIZE_MAX / elemSize)) + { + Interp_NOMEM(); + } + + return m_pCompiler->AllocMethodData(elems * elemSize); + } + + // Frees the block of memory pointed to by p. + virtual void Free(void* p) override + { + // Interpreter-FIXME: m_pCompiler->FreeMethodData + free(p); + } +}; + +// Interpreter-FIXME Use specific allocators for their intended purpose // Allocator for data that is kept alive throughout application execution, // being freed only if the associated method gets freed. void* InterpCompiler::AllocMethodData(size_t numBytes) @@ -807,6 +862,29 @@ void InterpCompiler::EmitCode() m_compHnd->setBoundaries(m_methodInfo->ftn, m_ILToNativeMapSize, m_pILToNativeMap); } +void InterpCompiler::BuildGCInfo(InterpMethod *pInterpMethod) +{ +#ifdef FEATURE_INTERPRETER + InterpIAllocator* pAllocator = new (this) InterpIAllocator(this); + InterpreterGcInfoEncoder* gcInfoEncoder = new (this) InterpreterGcInfoEncoder(m_compHnd, m_methodInfo, pAllocator, Interp_NOMEM); + assert(gcInfoEncoder); + + gcInfoEncoder->SetCodeLength(m_methodCodeSize); + + // TODO: Request slot IDs for all our locals before finalizing + + gcInfoEncoder->FinalizeSlotIds(); + + // TODO: Use finalized slot IDs to declare live ranges + + gcInfoEncoder->Build(); + + // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t) + // let's save the values anyway for debugging purposes + gcInfoEncoder->Emit(); +#endif +} + InterpMethod* InterpCompiler::CreateInterpMethod() { int numDataItems = m_dataItems.GetSize(); @@ -829,6 +907,9 @@ int32_t* InterpCompiler::GetCode(int32_t *pCodeSize) InterpCompiler::InterpCompiler(COMP_HANDLE compHnd, CORINFO_METHOD_INFO* methodInfo) { + // Fill in the thread-local used for assertions + t_InterpJitInfoTls = compHnd; + m_methodHnd = methodInfo->ftn; m_compScopeHnd = methodInfo->scope; m_compHnd = compHnd; @@ -1504,6 +1585,15 @@ bool InterpCompiler::EmitCallIntrinsics(CORINFO_METHOD_HANDLE method, CORINFO_SI return true; } } + else if (className && !strcmp(className, "GC")) + { + if (methodName && !strcmp(methodName, "Collect")) + { + AddIns(INTOP_GC_COLLECT); + // Not reducing the stack pointer because we expect the version with no arguments + return true; + } + } } return false; @@ -3379,3 +3469,17 @@ void InterpCompiler::PrintCompiledIns(const int32_t *ip, const int32_t *start) PrintInsData(NULL, insOffset, ip, opcode); printf("\n"); } + +extern "C" void assertAbort(const char* why, const char* file, unsigned line) +{ + if (t_InterpJitInfoTls) { + if (!t_InterpJitInfoTls->doAssert(file, line, why)) + return; + } + +#ifdef _MSC_VER + __debugbreak(); +#else // _MSC_VER + __builtin_trap(); +#endif // _MSC_VER +} diff --git a/src/coreclr/interpreter/compiler.h b/src/coreclr/interpreter/compiler.h index 4350e483494e88..8b603fa03f1be3 100644 --- a/src/coreclr/interpreter/compiler.h +++ b/src/coreclr/interpreter/compiler.h @@ -89,7 +89,7 @@ struct InterpInst { InterpBasicBlock *pTargetBB; // target basic block for branch instructions InterpBasicBlock **ppTargetBBTable; // basic block table for switch instruction - InterpCallInfo *pCallInfo; // additional information for call instructions + InterpCallInfo *pCallInfo; // additional information for call instructions } info; int32_t opcode; @@ -259,8 +259,12 @@ struct Reloc typedef class ICorJitInfo* COMP_HANDLE; +class InterpIAllocator; + class InterpCompiler { + friend class InterpIAllocator; + private: CORINFO_METHOD_HANDLE m_methodHnd; CORINFO_MODULE_HANDLE m_compScopeHnd; @@ -294,10 +298,12 @@ class InterpCompiler CORINFO_CLASS_HANDLE ResolveClassToken(uint32_t token); void* AllocMethodData(size_t numBytes); +public: // FIXME Mempool allocation currently leaks. We need to add an allocator and then // free all memory when method is finished compilling. void* AllocMemPool(size_t numBytes); void* AllocMemPool0(size_t numBytes); +private: void* AllocTemporary(size_t numBytes); void* AllocTemporary0(size_t numBytes); void* ReallocTemporary(void* ptr, size_t numBytes); @@ -430,8 +436,25 @@ class InterpCompiler InterpCompiler(COMP_HANDLE compHnd, CORINFO_METHOD_INFO* methodInfo); InterpMethod* CompileMethod(); + void BuildGCInfo(InterpMethod *pInterpMethod); int32_t* GetCode(int32_t *pCodeSize); }; +/***************************************************************************** + * operator new + * + * Uses the compiler's AllocMemPool0, which will eventually free automatically at the end of compilation (doesn't yet). + */ + + inline void* operator new(size_t sz, InterpCompiler* compiler) + { + return compiler->AllocMemPool0(sz); +} + + inline void* operator new[](size_t sz, InterpCompiler* compiler) + { + return compiler->AllocMemPool0(sz); + } + #endif //_COMPILER_H_ diff --git a/src/coreclr/interpreter/eeinterp.cpp b/src/coreclr/interpreter/eeinterp.cpp index 799bdbf490e488..338b429e78ee65 100644 --- a/src/coreclr/interpreter/eeinterp.cpp +++ b/src/coreclr/interpreter/eeinterp.cpp @@ -84,7 +84,7 @@ CorJitResult CILInterp::compileMethod(ICorJitInfo* compHnd, int32_t IRCodeSize; int32_t *pIRCode = compiler.GetCode(&IRCodeSize); - + // FIXME this shouldn't be here compHnd->setMethodAttribs(methodInfo->ftn, CORINFO_FLG_INTERPRETER); @@ -106,6 +106,9 @@ CorJitResult CILInterp::compileMethod(ICorJitInfo* compHnd, *entryAddress = (uint8_t*)args.hotCodeBlock; *nativeSizeOfCode = sizeOfCode; + // We can't do this until we've called allocMem + compiler.BuildGCInfo(pMethod); + return CORJIT_OK; } diff --git a/src/coreclr/interpreter/interpreter.h b/src/coreclr/interpreter/interpreter.h index 4977f185d2f3c2..f0a9fe898a878e 100644 --- a/src/coreclr/interpreter/interpreter.h +++ b/src/coreclr/interpreter/interpreter.h @@ -15,6 +15,7 @@ #ifdef HOST_WINDOWS #include #endif + #include #include "corhdr.h" diff --git a/src/coreclr/interpreter/intops.def b/src/coreclr/interpreter/intops.def index 61f0d20c9083da..84e429ff72f9ef 100644 --- a/src/coreclr/interpreter/intops.def +++ b/src/coreclr/interpreter/intops.def @@ -255,6 +255,7 @@ OPDEF(INTOP_CALL_HELPER_PP, "call.helper.pp", 5, 1, 0, InterpOpThreeInts) OPDEF(INTOP_ZEROBLK_IMM, "zeroblk.imm", 3, 0, 1, InterpOpInt) OPDEF(INTOP_BREAKPOINT, "breakpoint", 1, 0, 0, InterpOpNoArgs) OPDEF(INTOP_FAILFAST, "failfast", 1, 0, 0, InterpOpNoArgs) +OPDEF(INTOP_GC_COLLECT, "gc.collect", 1, 0, 0, InterpOpNoArgs) // All instructions after this point are IROPS, instructions that are not emitted/executed OPDEF(INTOP_NOP, "nop", 1, 0, 0, InterpOpNoArgs) diff --git a/src/coreclr/jit/error.cpp b/src/coreclr/jit/error.cpp index ff5273a3b391db..afd74afac25064 100644 --- a/src/coreclr/jit/error.cpp +++ b/src/coreclr/jit/error.cpp @@ -258,7 +258,7 @@ LogEnv::LogEnv(ICorJitInfo* aCompHnd) } /*****************************************************************************/ -extern "C" void __cdecl assertAbort(const char* why, const char* file, unsigned line) +extern "C" void assertAbort(const char* why, const char* file, unsigned line) { const char* msg = why; LogEnv* env = JitTls::GetLogEnv(); diff --git a/src/coreclr/jit/host.h b/src/coreclr/jit/host.h index d10eb93ca9a122..6f5b4427403e64 100644 --- a/src/coreclr/jit/host.h +++ b/src/coreclr/jit/host.h @@ -36,7 +36,7 @@ void gcDump_logf(const char* fmt, ...); void logf(unsigned level, const char* fmt, ...); -extern "C" void ANALYZER_NORETURN __cdecl assertAbort(const char* why, const char* file, unsigned line); +extern "C" void ANALYZER_NORETURN assertAbort(const char* why, const char* file, unsigned line); #undef assert #define assert(p) (void)((p) || (assertAbort(#p, __FILE__, __LINE__), 0)) diff --git a/src/coreclr/vm/codeman.cpp b/src/coreclr/vm/codeman.cpp index 7093f102e6d00b..d8e5805976a8da 100644 --- a/src/coreclr/vm/codeman.cpp +++ b/src/coreclr/vm/codeman.cpp @@ -3489,7 +3489,7 @@ GCInfoToken InterpreterJitManager::GetGCInfoToken(const METHODTOKEN& MethodToken SUPPORTS_DAC; } CONTRACTL_END; - // The JIT-ed code always has the current version of GCInfo + // The Interpreter IR always has the current version of GCInfo return{ GetCodeHeader(MethodToken)->GetGCInfo(), GCINFO_VERSION }; } #endif // FEATURE_INTERPRETER diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index dfb495e4e50114..189c2edca45f24 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -2310,8 +2310,112 @@ bool InterpreterCodeManager::EnumGcRefs(PREGDISPLAY pContext, LPVOID hCallBack, DWORD relOffsetOverride) { - // Interpreter-TODO: Implement this - return false; + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + unsigned curOffs = pCodeInfo->GetRelOffset(); + +#ifdef _DEBUG + // Get the name of the current method + const char * methodName = pCodeInfo->GetMethodDesc()->GetName(); + LOG((LF_GCINFO, LL_INFO1000, "Reporting GC refs for %s at offset %04x.\n", + methodName, curOffs)); +#endif + + GCInfoToken gcInfoToken = pCodeInfo->GetGCInfoToken(); + +#ifdef _DEBUG + if (flags & ActiveStackFrame) + { + InterpreterGcInfoDecoder _gcInfoDecoder( + gcInfoToken, + DECODE_INTERRUPTIBILITY, + curOffs + ); + _ASSERTE(_gcInfoDecoder.IsInterruptible() || _gcInfoDecoder.CouldBeSafePoint()); + } +#endif + + // Check if we have been given an override value for relOffset + if (relOffsetOverride != NO_OVERRIDE_OFFSET) + { + // We've been given an override offset for GC Info +#ifdef _DEBUG + InterpreterGcInfoDecoder _gcInfoDecoder( + gcInfoToken, + DECODE_CODE_LENGTH + ); + + // We only use override offset for wantsReportOnlyLeaf + _ASSERTE(_gcInfoDecoder.WantsReportOnlyLeaf()); +#endif // _DEBUG + + curOffs = relOffsetOverride; + + LOG((LF_GCINFO, LL_INFO1000, "Adjusted GC reporting offset to provided override offset. Now reporting GC refs for %s at offset %04x.\n", + methodName, curOffs)); + } + + +#if defined(FEATURE_EH_FUNCLETS) // funclets + if (pCodeInfo->GetJitManager()->IsFilterFunclet(pCodeInfo)) + { + // Filters are the only funclet that run during the 1st pass, and must have + // both the leaf and the parent frame reported. In order to avoid double + // reporting of the untracked variables, do not report them for the filter. + flags |= NoReportUntracked; + } +#endif // FEATURE_EH_FUNCLETS + + bool reportScratchSlots; + + // We report scratch slots only for leaf frames. + // A frame is non-leaf if we are executing a call, or a fault occurred in the function. + // The only case in which we need to report scratch slots for a non-leaf frame + // is when execution has to be resumed at the point of interruption (via ResumableFrame) + _ASSERTE( sizeof( BOOL ) >= sizeof( ActiveStackFrame ) ); + reportScratchSlots = (flags & ActiveStackFrame) != 0; + + + InterpreterGcInfoDecoder gcInfoDecoder( + gcInfoToken, + GcInfoDecoderFlags (DECODE_GC_LIFETIMES | DECODE_SECURITY_OBJECT | DECODE_VARARG), + curOffs + ); + + if (!gcInfoDecoder.EnumerateLiveSlots( + pContext, + reportScratchSlots, + flags, + pCallback, + hCallBack + )) + { + return false; + } + +#ifdef FEATURE_EH_FUNCLETS // funclets + // + // If we're in a funclet, we do not want to report the incoming varargs. This is + // taken care of by the parent method and the funclet should access those arguments + // by way of the parent method's stack frame. + // + if(pCodeInfo->IsFunclet()) + { + return true; + } +#endif // FEATURE_EH_FUNCLETS + + if (gcInfoDecoder.GetIsVarArg()) + { + // Interpreter-TODO: Implement this + _ASSERTE(false); + return false; + } + + return true; } OBJECTREF InterpreterCodeManager::GetInstance(PREGDISPLAY pContext, diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 39a06fa3e233fe..b08f1ebd5f0836 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -2285,4 +2285,8 @@ template void TGcInfoDecoder::ReportSt // Instantiate the decoder so other files can use it template class TGcInfoDecoder; +#ifdef FEATURE_INTERPRETER +template class TGcInfoDecoder; +#endif // FEATURE_INTERPRETER + #endif // USE_GC_INFO_DECODER diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 0b6ee5052ed2d9..cc46fd606f12df 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#include - #ifdef FEATURE_INTERPRETER +#include "threads.h" +#include "gcenv.h" #include "interpexec.h" typedef void* (*HELPER_FTN_PP)(void*); @@ -1105,6 +1105,17 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr memset(LOCAL_VAR(ip[1], void*), 0, ip[2]); ip += 3; break; + case INTOP_GC_COLLECT: { + // HACK: blocking gc of all generations to enable early stackwalk testing + // Interpreter-TODO: Remove this + { + pInterpreterFrame->SetTopInterpMethodContextFrame(pFrame); + GCX_COOP(); + GCHeapUtilities::GetGCHeap()->GarbageCollect(-1, false, 0x00000002); + } + ip++; + break; + } case INTOP_FAILFAST: assert(0); break; diff --git a/src/native/corehost/apphost/static/CMakeLists.txt b/src/native/corehost/apphost/static/CMakeLists.txt index 039fa832be3d1e..e7103871b0ef7a 100644 --- a/src/native/corehost/apphost/static/CMakeLists.txt +++ b/src/native/corehost/apphost/static/CMakeLists.txt @@ -214,6 +214,10 @@ else() endif() +LIST(APPEND NATIVE_LIBS + gcinfo +) + if(CLR_CMAKE_TARGET_APPLE) LIST(APPEND NATIVE_LIBS System.Security.Cryptography.Native.Apple-Static diff --git a/src/tests/JIT/interpreter/Interpreter.cs b/src/tests/JIT/interpreter/Interpreter.cs index d70afcec888728..3d61a3a782c383 100644 --- a/src/tests/JIT/interpreter/Interpreter.cs +++ b/src/tests/JIT/interpreter/Interpreter.cs @@ -78,7 +78,7 @@ static int Main(string[] args) [MethodImpl(MethodImplOptions.NoInlining)] public static void RunInterpreterTests() { -// Console.WriteLine("Run interp tests"); + // Console.WriteLine("Run interp tests"); if (SumN(50) != 1275) Environment.FailFast(null); if (Mul4(53, 24, 13, 131) != 2166216) @@ -88,6 +88,7 @@ public static void RunInterpreterTests() if (!PowLoop(20, 10, 1661992960)) Environment.FailFast(null); + if (!TestJitFields()) Environment.FailFast(null); // Disable below tests because they are potentially unstable since they do allocation @@ -100,6 +101,9 @@ public static void RunInterpreterTests() Environment.FailFast(null); // if (!TestVirtual()) // Environment.FailFast(null); + + // For stackwalking validation + System.GC.Collect(); } public static int Mul4(int a, int b, int c, int d)