зеркало из https://github.com/dotnet/llilc.git
Add initial support for JIT options
Makes use of the new CLR/EE interface to extract environment variables. Initial support only covers COMPLUS_AltJit and COMPLUS_DUMPLLVMIR. * Adds Options container in the JITContext. This is used to interface with CoreCLR and hold any state. Currently implements the AltJit detection as well as DumpLevel(COMPlus_DUMPLLVMIR) and OptLevel. * Includes basic method set support that will be used to implement matching of methods. * Adds simple utility functions to do cross platform UTF16 to UTF8 conversions. This is based on the facilities supplied by LLVM. * Fix testing scripts to prepend 'COMPlus_' on the DUMPLLVMIR variable.
This commit is contained in:
Родитель
fda6100d1a
Коммит
67236e4f93
|
@ -17,20 +17,13 @@
|
|||
#define LLILC_JIT_H
|
||||
|
||||
#include "Pal/LLILCPal.h"
|
||||
#include "options.h"
|
||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/ThreadLocal.h"
|
||||
|
||||
class ABIInfo;
|
||||
|
||||
/// \brief Enum for LLVM IR Dump Level
|
||||
enum LLVMDumpLevel {
|
||||
NODUMP, ///< Do not dump any LLVM IR or summary.
|
||||
SUMMARY, ///< Only dump one line summary per method.
|
||||
VERBOSE ///< Dump full LLVM IR and method summary.
|
||||
};
|
||||
|
||||
struct LLILCJitPerThreadState;
|
||||
|
||||
/// \brief This struct holds per-jit request state.
|
||||
|
@ -38,7 +31,7 @@ struct LLILCJitPerThreadState;
|
|||
/// LLILC is invoked to jit one method at a time. An \p LLILCJitContext
|
||||
/// represents all of the information about a particular jit request.
|
||||
///
|
||||
/// Note the Jit can be re-entered on the same thread thread while in the
|
||||
/// Note the Jit can be re-entered on the same thread while in the
|
||||
/// middle of jitting a method, if other methods must be run and also require
|
||||
/// jitting. To handle this, the contexts form a stack, so that the jit can
|
||||
/// keep the state from nested jit requests distinct from parent requests.
|
||||
|
@ -101,6 +94,11 @@ public:
|
|||
LLILCJitPerThreadState *State; ///< Per thread state for the jit.
|
||||
//@}
|
||||
|
||||
/// \name Per invocation JIT Options
|
||||
//@{
|
||||
Options Options;
|
||||
//@}
|
||||
|
||||
/// \name Jit output sizes
|
||||
//@{
|
||||
uint32_t HotCodeSize = 0; ///< Size of hot code section in bytes.
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
//===----------------- include/Jit/options.h -------------------*- C++ -*-===//
|
||||
//
|
||||
// LLILC
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Declaration of the Options object to encapsulate JIT options
|
||||
/// extracted from CoreCLR config values.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
struct LLILCJitContext;
|
||||
|
||||
/// \brief Enum for JIT optimization level.
|
||||
enum OptLevel {
|
||||
INVALID,
|
||||
DEBUG_CODE, ///< No/Low optimization to preserve debug semantics.
|
||||
BLENDED_CODE, ///< Fast code that remains sensitive to code size.
|
||||
SMALL_CODE, ///< Optimized for small size.
|
||||
FAST_CODE ///< Optimized for speed.
|
||||
};
|
||||
|
||||
/// \brief Enum for LLVM IR Dump Level
|
||||
enum LLVMDumpLevel {
|
||||
NODUMP, ///< Do not dump any LLVM IR or summary.
|
||||
SUMMARY, ///< Only dump one line summary per method.
|
||||
VERBOSE ///< Dump full LLVM IR and method summary.
|
||||
};
|
||||
|
||||
/// \brief The JIT options provided via CoreCLR configurations.
|
||||
///
|
||||
/// This class implements the JIT options flags. The CoreCLR interface
|
||||
/// is queried and results are cached in this object.
|
||||
///
|
||||
class Options {
|
||||
public:
|
||||
/// Construct an Options object based on the passed JitContext.
|
||||
Options(LLILCJitContext *Context);
|
||||
|
||||
/// Destruct Options object.
|
||||
~Options();
|
||||
|
||||
// Initialize object after full Context is established.
|
||||
void initialize();
|
||||
|
||||
/// Set current JIT invocation as "AltJit". This sets up
|
||||
/// the JIT to filter based on the AltJit flag contents.
|
||||
void setIsAltJit();
|
||||
|
||||
/// \brief Compute dump level for the JIT
|
||||
///
|
||||
/// Dump level requested via CLR config for the JIT
|
||||
void setDumpLevel();
|
||||
|
||||
/// \brief Set optimization level for the JIT.
|
||||
///
|
||||
/// Opt Level based on CLR provided flags and environment.
|
||||
void setOptLevel();
|
||||
|
||||
public:
|
||||
LLILCJitContext *Context; ///< Invocation CLR Execution Engine flags.
|
||||
LLVMDumpLevel DumpLevel; ///< Dump level for this JIT invocation.
|
||||
OptLevel OptLevel; ///< Optimization level for this JIT invocation.
|
||||
bool IsAltJit; ///< True if compiling as the alternative JIT.
|
||||
|
||||
private:
|
||||
static MethodSet AltJitMethodSet; ///< Singleton AltJit MethodSet.
|
||||
static MethodSet AltJitNgenMethodSet; ///< Singleton AltJitNgen MethodSet.
|
||||
};
|
||||
|
||||
#endif // OPTIONS_H
|
|
@ -0,0 +1,116 @@
|
|||
//===----------------- include/Jit/utility.h -------------------*- C++ -*-===//
|
||||
//
|
||||
// LLILC
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// \brief Declarations of utility classes.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef UTILITY_H
|
||||
#define UTILITY_H
|
||||
|
||||
#include <list>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "llvm/Support/Atomic.h"
|
||||
#include "llvm/Config/llvm-config.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
/// \brief MethodName struct representing a particular method on a type.
|
||||
///
|
||||
/// MethodNames are the elements of MethodSet and are used to do filtering of
|
||||
/// methods when running as an "alt" JIT.
|
||||
struct MethodName {
|
||||
/// Name
|
||||
std::unique_ptr<std::string> Name;
|
||||
/// Class name
|
||||
std::unique_ptr<std::string> ClassName;
|
||||
/// Number of method arguments.
|
||||
int NumArgs;
|
||||
|
||||
/// Default constructor.
|
||||
MethodName() : Name(nullptr), ClassName(nullptr), NumArgs(0) {}
|
||||
|
||||
/// Copy constructor for move semantics.
|
||||
MethodName(MethodName &&M) {
|
||||
Name = std::move(M.Name);
|
||||
ClassName = std::move(M.ClassName);
|
||||
NumArgs = M.NumArgs;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief MethodSet comprising a set of MethodName objects
|
||||
///
|
||||
/// Method set containing methods to filter for if running as the "alt" JIT.
|
||||
class MethodSet {
|
||||
public:
|
||||
/// Test if the MethodSet is empty.
|
||||
/// \returns true if MethodSet is empty.
|
||||
bool isEmpty() {
|
||||
assert(this->isInitialized());
|
||||
|
||||
return (this->MethodList->size() == 0);
|
||||
}
|
||||
|
||||
/// Test of MethodSet contains Method.
|
||||
/// \param MethodName Name of the method
|
||||
/// \param ClassName Encapsulating class of the method.
|
||||
/// \param sig Pointer to method signature.
|
||||
/// \return true if Method is contained in Set.
|
||||
bool contains(const char *MethodName, const char *ClassName,
|
||||
PCCOR_SIGNATURE sig);
|
||||
|
||||
void init(std::unique_ptr<std::string> ConfigValue) {
|
||||
if (this->isInitialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ISSUE-TODO:1/16/2015 - Parse config value returned by the CLR
|
||||
// into methods if any.
|
||||
|
||||
MethodName M;
|
||||
|
||||
M.Name = std::move(ConfigValue);
|
||||
|
||||
std::list<MethodName> *ML = new std::list<MethodName>;
|
||||
|
||||
ML->push_front(std::move(M));
|
||||
|
||||
// This write should be atomic, delete if we're not the first.
|
||||
|
||||
sys::cas_flag Value =
|
||||
sys::CompareAndSwap((sys::cas_flag *)&(this->Initialized), 0x1, 0x0);
|
||||
|
||||
if (Value != 0x0) {
|
||||
delete ML;
|
||||
} else {
|
||||
this->MethodList = ML;
|
||||
}
|
||||
}
|
||||
|
||||
bool isInitialized() { return MethodList != nullptr; }
|
||||
|
||||
private:
|
||||
/// Internal initialized flag. This should only be accessed via
|
||||
/// interlocked exchange operations.
|
||||
uint32_t Initialized = 0;
|
||||
/// MethodList implementing the set.
|
||||
std::list<MethodName> *MethodList = nullptr;
|
||||
};
|
||||
|
||||
/// \brief Class implementing a platform independent wchar_t to utf8.
|
||||
class Convert {
|
||||
public:
|
||||
static std::unique_ptr<std::string> wideToUtf8(const wchar_t *wideStr);
|
||||
};
|
||||
|
||||
#endif // UTILITY_H
|
|
@ -249,19 +249,18 @@ public:
|
|||
// private:
|
||||
union {
|
||||
// Right now m_bits is for debugging,
|
||||
// clang-format off
|
||||
struct {
|
||||
TITypes Type : 6;
|
||||
uint32_t UninitObj : 1; // used
|
||||
uint32_t ByRef : 1; // used
|
||||
uint32_t ByRefReadOnly : 1;
|
||||
// clang-format off
|
||||
uint32_t: 3; // unused?
|
||||
uint32_t ThisPtr : 1; // used
|
||||
uint32_t: 1; // unused?
|
||||
// clang-format on
|
||||
uint32_t GenericTypeVar : 1; // used
|
||||
} Bits;
|
||||
|
||||
// clang-format on
|
||||
uint32_t Flags;
|
||||
};
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@ add_llilcjit_library(
|
|||
jitpch.cpp
|
||||
LLILCJit.cpp
|
||||
EEMemoryManager.cpp
|
||||
options.cpp
|
||||
utility.cpp
|
||||
${LLILCJIT_EXPORTS_DEF}
|
||||
)
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "jitpch.h"
|
||||
#include "LLILCJit.h"
|
||||
#include "options.h"
|
||||
#include "readerir.h"
|
||||
#include "abi.h"
|
||||
#include "EEMemoryManager.h"
|
||||
|
@ -132,7 +133,7 @@ void LLILCJitContext::outputDebugMethodName() {
|
|||
}
|
||||
|
||||
LLILCJitContext::LLILCJitContext(LLILCJitPerThreadState *PerThreadState)
|
||||
: State(PerThreadState), HasLoadedBitCode(false) {
|
||||
: State(PerThreadState), HasLoadedBitCode(false), Options(this) {
|
||||
this->Next = State->JitContext;
|
||||
State->JitContext = this;
|
||||
}
|
||||
|
@ -182,6 +183,11 @@ CorJitResult LLILCJit::compileMethod(ICorJitInfo *JitInfo,
|
|||
Context.CurrentModule->setTargetTriple(LLVM_DEFAULT_TARGET_TRIPLE);
|
||||
Context.TheABIInfo = ABIInfo::get(*Context.CurrentModule);
|
||||
|
||||
// Initialize per invocation JIT options. This should be done after the
|
||||
// rest of the Context is filled out as it has dependencies on Flags and
|
||||
// MethodInfo
|
||||
Context.Options.initialize();
|
||||
|
||||
EngineBuilder Builder(std::move(M));
|
||||
std::string ErrStr;
|
||||
Builder.setErrorStr(&ErrStr);
|
||||
|
@ -194,7 +200,7 @@ CorJitResult LLILCJit::compileMethod(ICorJitInfo *JitInfo,
|
|||
// Statepoint GC does not support FastIsel yet.
|
||||
Options.EnableFastISel = false;
|
||||
|
||||
if ((Flags & CORJIT_FLG_DEBUG_CODE) == 0) {
|
||||
if (Context.Options.OptLevel != OptLevel::DEBUG_CODE) {
|
||||
Builder.setOptLevel(CodeGenOpt::Level::Default);
|
||||
} else {
|
||||
Builder.setOptLevel(CodeGenOpt::Level::None);
|
||||
|
@ -214,7 +220,7 @@ CorJitResult LLILCJit::compileMethod(ICorJitInfo *JitInfo,
|
|||
|
||||
// Now jit the method.
|
||||
CorJitResult Result = CORJIT_INTERNALERROR;
|
||||
if (dumpLevel() == VERBOSE) {
|
||||
if (Context.Options.DumpLevel == VERBOSE) {
|
||||
Context.outputDebugMethodName();
|
||||
}
|
||||
bool HasMethod = this->readMethod(&Context);
|
||||
|
@ -322,7 +328,7 @@ bool LLILCJit::readMethod(LLILCJitContext *JitContext) {
|
|||
return true;
|
||||
}
|
||||
|
||||
LLVMDumpLevel DumpLevel = dumpLevel();
|
||||
LLVMDumpLevel DumpLevel = JitContext->Options.DumpLevel;
|
||||
|
||||
LLILCJitPerThreadState *PerThreadState = State.get();
|
||||
GenIR Reader(JitContext, &PerThreadState->ClassTypeMap,
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
#include "global.h"
|
||||
#include "jitpch.h"
|
||||
#include "LLILCJit.h"
|
||||
#include "options.h"
|
||||
|
||||
// For now we're always running as the altjit
|
||||
#define ALT_JIT 1
|
||||
|
||||
// These are the instantiations of the static method sets in Options.h.
|
||||
// This MethodSets are initialized from CLRConfig values passed through
|
||||
// the corinfo.h interface.
|
||||
|
||||
MethodSet Options::AltJitMethodSet;
|
||||
MethodSet Options::AltJitNgenMethodSet;
|
||||
|
||||
Options::Options(LLILCJitContext *Context) : Context(Context) {}
|
||||
|
||||
void Options::initialize() {
|
||||
// Set 'IsAltJit' based on environment information.
|
||||
this->setIsAltJit();
|
||||
|
||||
// Set dump level for this JIT invocation.
|
||||
this->setDumpLevel();
|
||||
|
||||
// Set optimization level for this JIT invocation.
|
||||
this->setOptLevel();
|
||||
}
|
||||
|
||||
void Options::setIsAltJit() {
|
||||
// Initial state is that we are not an alternative jit until proven otherwise;
|
||||
|
||||
this->IsAltJit = false;
|
||||
|
||||
// NDEBUG is !Debug
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
|
||||
// DEBUG case
|
||||
|
||||
// Get/reuse method set that contains the altjit method value.
|
||||
MethodSet *AltJit = nullptr;
|
||||
|
||||
if (this->Context->Flags & CORJIT_FLG_PREJIT) {
|
||||
wchar_t *NgenStr =
|
||||
this->Context->JitInfo->getStringConfigValue(L"AltJitNgen");
|
||||
std::unique_ptr<std::string> NgenUtf8 = Convert::wideToUtf8(NgenStr);
|
||||
this->AltJitNgenMethodSet.init(std::move(NgenUtf8));
|
||||
this->Context->JitInfo->freeStringConfigValue(NgenStr);
|
||||
AltJit = &AltJitNgenMethodSet;
|
||||
} else {
|
||||
wchar_t *JitStr = this->Context->JitInfo->getStringConfigValue(L"AltJit");
|
||||
// Move this to the UTIL code and ifdef it for platform
|
||||
std::unique_ptr<std::string> JitUtf8 = Convert::wideToUtf8(JitStr);
|
||||
this->AltJitMethodSet.init(std::move(JitUtf8));
|
||||
this->Context->JitInfo->freeStringConfigValue(JitStr);
|
||||
AltJit = &AltJitMethodSet;
|
||||
}
|
||||
|
||||
#ifdef ALT_JIT
|
||||
if (AltJit->contains(this->Context->MethodName.data(), nullptr,
|
||||
this->Context->MethodInfo->args.pSig)) {
|
||||
this->IsAltJit = true;
|
||||
}
|
||||
#endif // ALT_JIT
|
||||
|
||||
#else
|
||||
if (this->Context->Flags & CORJIT_FLG_PREJIT) {
|
||||
wchar_t *NgenStr =
|
||||
this->Context->JitInfo->getStringConfigValue(L"AltJitNgen");
|
||||
std::unique_ptr<std::string> NgenUtf8 = Convert::wideToUtf8(NgenStr);
|
||||
if (NgenUtf8->compare("*") == 0) {
|
||||
this->IsAltJit = true;
|
||||
}
|
||||
} else {
|
||||
wchar_t *JitStr = this->Context->JitInfo->getStringConfigValue(L"AltJit");
|
||||
std::unique_ptr<std::string> JitUtf8 = Convert::wideToUtf8(JitStr);
|
||||
if (JitUtf8->compare("*") == 0) {
|
||||
this->IsAltJit = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Options::setDumpLevel() {
|
||||
wchar_t *LevelWStr =
|
||||
this->Context->JitInfo->getStringConfigValue(L"DUMPLLVMIR");
|
||||
if (LevelWStr) {
|
||||
std::unique_ptr<std::string> Level = Convert::wideToUtf8(LevelWStr);
|
||||
std::transform(Level->begin(), Level->end(), Level->begin(), ::toupper);
|
||||
if (Level->compare("VERBOSE") == 0) {
|
||||
this->DumpLevel = VERBOSE;
|
||||
return;
|
||||
}
|
||||
if (Level->compare("SUMMARY") == 0) {
|
||||
this->DumpLevel = SUMMARY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->DumpLevel = NODUMP;
|
||||
}
|
||||
|
||||
void Options::setOptLevel() {
|
||||
// Currently we only check for the debug flag but this will be extended
|
||||
// to account for further opt levels as we move forward.
|
||||
if ((this->Context->Flags & CORJIT_FLG_DEBUG_CODE) != 0) {
|
||||
this->OptLevel = OptLevel::DEBUG_CODE;
|
||||
}
|
||||
}
|
||||
|
||||
Options::~Options() {}
|
|
@ -0,0 +1,40 @@
|
|||
// Utility code
|
||||
|
||||
#include "global.h"
|
||||
#include "jitpch.h"
|
||||
|
||||
#include "utility.h"
|
||||
#include "llvm/Support/ConvertUTF.h"
|
||||
|
||||
// Checks given parsed representation to see if method is in the set.
|
||||
|
||||
bool MethodSet::contains(const char *Name, const char *ClassName,
|
||||
PCCOR_SIGNATURE sig) {
|
||||
assert(this->isInitialized());
|
||||
|
||||
std::list<MethodName>::const_iterator iterator;
|
||||
|
||||
for (iterator = this->MethodList->begin();
|
||||
iterator != this->MethodList->end(); ++iterator) {
|
||||
if (iterator->Name->compare("*") == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Just matching method name. This is way too loose but have not yet
|
||||
// plumbed through the class name and wildcard logic.
|
||||
|
||||
if (iterator->Name->compare(Name) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<std::string> Convert::wideToUtf8(const wchar_t *WideStr) {
|
||||
ArrayRef<char> SrcBytes((const char *)WideStr, (2 * wcslen(WideStr)));
|
||||
std::unique_ptr<std::string> OutString(new std::string);
|
||||
llvm::convertUTF16ToUTF8String(SrcBytes, *OutString);
|
||||
|
||||
return OutString;
|
||||
}
|
|
@ -933,11 +933,11 @@ function Global:RunTest
|
|||
}
|
||||
|
||||
# Protect old value and set the new value for DUMPLLVMIR
|
||||
$DumpExists = Test-Path Env:\DUMPLLVMIR
|
||||
$DumpExists = Test-Path Env:\COMPLus_DUMPLLVMIR
|
||||
if ($DumpExists) {
|
||||
$OldDump = $Env:DUMPLLVMIR
|
||||
$OldDump = $Env:COMPlus_DUMPLLVMIR
|
||||
}
|
||||
$Env:DUMPLLVMIR = $Dump
|
||||
$Env:COMPlus_DUMPLLVMIR = $Dump
|
||||
|
||||
# The set of test cases that are currently ignored
|
||||
$Env:SkipTestAssemblies = "Common;Exceptions;GC;Loader;managed;packages;Regressions;runtime;Tests;TestWrappers_x64_release;Threading"
|
||||
|
@ -950,7 +950,7 @@ function Global:RunTest
|
|||
|
||||
# Restore old value for DUMPLLVMIR
|
||||
if ($DumpExists) {
|
||||
$Env:DUMPLLVMIR = $OldDump;
|
||||
$Env:COMPlus_DUMPLLVMIR = $OldDump;
|
||||
}
|
||||
|
||||
# Restore the old jit
|
||||
|
|
Загрузка…
Ссылка в новой задаче