Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

D dynamic compilation support #2293

Merged
merged 93 commits into from
Nov 11, 2017

Conversation

Hardcode84
Copy link
Contributor

@Hardcode84 Hardcode84 commented Aug 27, 2017

Here is current state of my 'jit' work. Simple examples work but everything mostly untested (and I'm not sure how properly test it). Tested on win64 and linux64. IR preparation for jit mostly in gen/runtimecompile.cpp. Jit itself implemented as shared library in runtime/rtcompile-rt/cpp-so/, it was mostly copypasted from llvm examples and I'm not sure it 100% correct.

Status:

Tests:

  • I am using existing lit infrastructure, not sure it is best suitable here
  • It is hard to check functions were actually dynamically compiled (if we compile them statically most of the tests still pass)
  • Need more tests

Platform support:

Win64:

  • Thread locals in dynamic code doesn't work
Relocation type not implemented yet!
UNREACHABLE executed at c:\ldc\llvm\lib\executionengine\runtimedyld\Targets/Runt
imeDyldCOFFX86_64.h:104!

Linux:

  • Thread locals in dynamic code doesn't work
LLVM ERROR: Cannot select: 0x1007ec8: i64 = X86ISD::WrapperRIP TargetGlobalTLSAddress:i64<i64* @_D12thread_local8threadIdm> 0 [TF=10]
  0x1007e60: i64 = TargetGlobalTLSAddress<i64* @_D12thread_local8threadIdm> 0 [TF=10]

MacOS:

  • Thread locals doesn't work

Areas of improvement:

  • Revise dynamic code optimization (copypasted from ldc/clang for now)
  • Research hotpatching for dynamic code dispatch (does llvm support this?)
  • Option to limit dynamic functions search depth
  • Extend @runtimeCompile attribute to allow mark some functions always non-dynamic
  • Get rid of thunks when calling dynamic functions from other dynamic functions
  • Check dynamic compilation with code coverage (theoretically it should work)
  • Check dynamic compilation with PGO (dynamic compiler can use PGO info collected offline)
  • Research into jitted partial application, eg:
@rumtimeCompile int foo(int a, int b)
{
  if (b == 1)
  {
    return a * 42;
  }
  return a + 42;
}


// Generate another function which interprets 'b' as constant allow llvm optimizer to drop branch completely
auto fun = apply!foo(placeholder, 0);
return fun(5); // foo(5, 0)
  • Throw exception when attempted to call dynamic functions without compilation

@Hardcode84
Copy link
Contributor Author

Related druntime PR: ldc-developers/druntime#101

.gitmodules Outdated
@@ -1,6 +1,7 @@
[submodule "druntime"]
path = runtime/druntime
url = https://github.com/ldc-developers/druntime.git
url = https://github.com/Hardcode84/druntime.git
branch = runtime_compile
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this, I'll pull the druntime PR as soon as you add a doc comment to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

ddmd/globals.h Outdated
@@ -234,6 +234,8 @@ struct Param
uint32_t hashThreshold; // MD5 hash symbols larger than this threshold (0 = no hashing)

bool outputSourceLocations; // if true, output line tables.

bool enableRuntimeCompile;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small comment like the above would be nice.

@Hardcode84
Copy link
Contributor Author

Hardcode84 commented Aug 29, 2017

So here some design overview of dynamic compilation feature

@runtimeCompie attribute can be applied to any function (including lambdas and class virtual methods)

Compiler part:

  1. In DtoDeclareFunction read function uda's, if there is a @runtimeCompie attribute set IrFunction::runtimeCompile = true
  2. If IrFunction::runtimeCompile == true, call for this function declareRuntimeCompiledFunction which will create thunk function rtCompileFunc
    rtCompileFunc has same signature and attributes as the original function and any attemt to call or take address of the this function will be redirected to rtCompileFunc in DtoCallee
  3. In DtoDefineFunction call defineRuntimeCompiledFunction for any function which have runtimeCompile == true
  4. In defineRuntimeCompiledFunction create global variable which will store pointer to compiled function and create thunk function body which just calls this global var
    (TODO: It is possible to get rid of this global var, we just need to hotpatch code in runtime with compiled code address, is it worth it? how to do this in llvm?)
  5. In writeAndFreeLLModule call function generateBitcodeForRuntimeCompile for each module
  6. In generateBitcodeForRuntimeCompile first recursively search functions calls inside functions body, starting with @runtimeCompie functions.
    After that we have two lists of functions:
  • Directly marked by user @runtimeCompie
  • Not marked by user but used by @runtimeCompie functions directly or indirectly and with body available
    Both lists are subject to dynamic compilation. For the first list dynamic version will be always used. For the second static version will be used when called from static code and dynamic version when called from dynamic code.
    Also we now have a list of symbols required by dynamic functions (variables and external functions)
    (TODO: Option for limit recursion depth?)
  1. Clone module via llvm::CloneModule keeping only functions marked for dynamic compilations (directly or indirectly)
  2. Optimize dynamic functions thunks in cloned module, make thunk global var constant and initialize it with function (fixupRtThunks) (TODO: llvm optimizer can handle this but better to remove these thunks completely in dynamic code)
  3. Create module data required for dynamic compilation
/// Symbol required by dynamic code
struct RtCompileSymList
{
  const char* name; // Symbol name
  void* sym;        // Symbol address
};

/// Dynamic function
struct RtCompileFuncList
{
  const char* name; // Function name
  void* func;       // Thunk global var address to store compiled function pointer
};

/// Runtime compiled functions info per module
/// organized in linked list 
/// starting with `RuntimeCompileModulesHeadName` (defined in runtime)
struct RtComileModuleList
{
  RtComileModuleList* next; // Next module
  const void* irData;       // Ir data
  int32 irDataSize;         // Ir data size in bytes
  RtCompileFuncList* funcList; // Dynamic functions
  int32 funcListSize;            // Dynamic functions count
  RtCompileSymList* symList;   // Symbols
  int32 symListSize;             // Symbols count
};
  1. Add module constructor which registers RtComileModuleList into global linked list

Runtime part:

  1. Defines runtime compiled moduleds list head
  2. Defines rtCompileProcessImplSo function which do actual compilation
  3. rtCompileProcessImplSo for each module in list:
  4. Parses ir data into llvm module
  5. Optimizes module (TODO: optimization setup copypasted from clang and LDC itself and need to revise)
  6. Add modules to jit (copypasted from llvm tutorials), resolve functions using RtComileModuleList data and update thunk vars

@dnadlinger
Copy link
Member

Thanks a lot for this, this is impressive work! I hope I can find time for a closer review soon, but I'm sure the others will weigh in as well if you just ping them often enough. Do you keep a todo-list somewhere looking at actually shipping this on multiple OSes, etc.?

@Hardcode84
Copy link
Contributor Author

I tested this on linux and there is some issue with thread locals, except that everything seems to work.
I don't have device to test on macOS.

First comment updated.

ddmd/globals.h Outdated
@@ -234,6 +234,8 @@ struct Param
uint32_t hashThreshold; // MD5 hash symbols larger than this threshold (0 = no hashing)

bool outputSourceLocations; // if true, output line tables.

bool enableRuntimeCompile; // Enable dynamic compilation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not needed (we try to reduce the frontend diff (i.e. changes in ./ddmd/) as much as possible). Instead you should define cl::opt<bool> enableRuntimeCompile, such that it does not use "external" storage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -0,0 +1,93 @@
if(LDC_RUNTIME_COMPILE)
find_package(LLVM 3.9 REQUIRED support core irreader executionengine passes target nativecodegen)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this conflicts with https://github.com/ldc-developers/ldc/blob/master/CMakeLists.txt#L22 and thus prevents successful linking.

@@ -600,11 +601,17 @@ macro(build_all_runtime_variants d_flags c_flags ld_flags path_suffix outlist_ta
get_target_suffix("" "${path_suffix}" target_suffix)
set_common_library_properties(ldc-profile-rt${target_suffix})
endif()

build_jit_runtime ("${d_flags}" "${c_flags}" "${ld_flags}" "${path_suffix}" ${outlist_targets})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason you're not including ${D_FLAGS} and maybe ${D_FLAGS_RELEASE} here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

@@ -1,5 +1,5 @@
if(LDC_RUNTIME_COMPILE)
find_package(LLVM 3.9 REQUIRED support core irreader executionengine passes target nativecodegen)
find_package(LLVM 3.7 REQUIRED support core irreader executionengine passes target nativecodegen)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was that invoking find_package() twice probably doesn't work and that conditionally including your additional libs in the single invokation should, although at the cost of less modularity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed another thing, it fail to find 32-bit llvm libraries when MULTILIB is ON, but as I understand compiler itself will still be 64 bit in this case. Does these machines have 32-bit llvm libs?

Copy link
Member

@kinke kinke Sep 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler will be 64-bit, no 32-bit LLVM available and the 32-bit libs definitely shouldn't be needed; only the LDC executable (edit: and tools/utils? all 64-bit anyway) needs to be linked to LLVM. So this would indicate that some compiler/linker flags leak into other build targets (e.g., the 32-bit shared runtime libs).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do dynamic compilation through llvm in my jit runtime shared library, which then linked to the target application, and if this application is 32-bit then jit runtime and llvm libs also have to be 32-bit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes that makes perfect sense. I guess you'll have to build the native JIT runtime library exclusively then for now, excluding the 32-bit one for MULTILIB builds.

@@ -340,10 +340,12 @@ void ArgsBuilder::build(llvm::StringRef outputPath,
args.push_back("-lldc-profile-rt");
}

if (global.params.enableRuntimeCompile) {
#if defined(LDC_RUNTIME_COMPILE)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: to prevent all these #if things, you could create a function isRuntimeCompileEnabled() similar to isUsingLTO().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@Hardcode84
Copy link
Contributor Author

druntime submodule update:
#2404

@kinke
Copy link
Member

kinke commented Nov 9, 2017

Why a separate PR? Just update the submodule in here.

@Hardcode84
Copy link
Contributor Author

ok

@Hardcode84
Copy link
Contributor Author

Renamed EVERYWHERE

@kinke
Copy link
Member

kinke commented Nov 10, 2017

Sorry to bother you hopefully one last time, but there's still docs/runtime_compile.md left to be renamed. ;)

CMakeLists.txt Outdated
#
set(LDC_DYNAMIC_COMPILE False) # must be a valid Python boolean constant (case sensitive)
if (NOT (LDC_LLVM_VER LESS 400))
message(STATUS "Building LDC with runtime compilation support")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"with dynamic compilation support" for consistency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

LICENSE Outdated
@@ -23,6 +23,9 @@ Libraries (lib/ and import/ in binary packages):
Phobos (runtime/phobos), is distributed under the terms of the Boost
Software License. See the individual source files for author information.

- The jit runtime library is distributed under the terms of the Boost
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd make this "The ldc-jit-rt [or whatever library it really is] library is distributed under the terms of the Boost Software license (but additionally makes use of LLVM code)." or something like that for clarity – unless I'm missing something, people need to distribute the LLVM copyright notice when they are using dynamic compile support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

var->setConstant(true);
var->setInitializer(initializer);
var->setLinkage(llvm::GlobalValue::PrivateLinkage);
// auto tempVar = new llvm::GlobalVariable(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the deal with this (and other) commented out sections?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

/**
* Contains dynamic compilation API.
*
* Copyright: Authors 2017
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably at least say something vague like "the LDC team", or your name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was copypasted from somewhere, fixed to "the LDC team". But I don't like these 'authors' fields because everyone forget to update them.

@Hardcode84
Copy link
Contributor Author

More renamings and cleanups

@kinke
Copy link
Member

kinke commented Nov 10, 2017

Thanks Ivan. I'll merge this tomorrow afternoon evening (CET) before tagging v1.6.0-beta1 if there are no objections.

@kinke kinke merged commit e8405a1 into ldc-developers:master Nov 11, 2017
@dnadlinger
Copy link
Member

Ouch, I would have very much preferred this being merged in less than 93 commits.

@kinke
Copy link
Member

kinke commented Nov 11, 2017

Feasible options being 1 or 93, I opted for the full bunch. I'll gladly let others make decisions like this in the future, I gave a 24+h notice.

@dnadlinger
Copy link
Member

It's not a huge thing either way, but there is also no need to react in a snarky fashion.

@Hardcode84
Copy link
Contributor Author

Thanks for review. Actually, I wanted to ask about squashing but I forgot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants