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:
rhadley 2015-02-25 14:56:44 -08:00
Родитель fda6100d1a
Коммит 67236e4f93
9 изменённых файлов: 373 добавлений и 20 удалений

Просмотреть файл

@ -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.

81
include/Jit/options.h Normal file
Просмотреть файл

@ -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

116
include/Jit/utility.h Normal file
Просмотреть файл

@ -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,

111
lib/Jit/options.cpp Normal file
Просмотреть файл

@ -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() {}

40
lib/Jit/utility.cpp Normal file
Просмотреть файл

@ -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