jckarter / clay

The Clay programming language
http://claylabs.com/clay
Other
404 stars 34 forks source link

Pack global state #487

Closed galchinsky closed 11 years ago

galchinsky commented 11 years ago

Every global, static and static-in-function variable is packed to CompilerState struct https://github.com/galchinsky/clay/blob/packGlobalState/compiler/clay.hpp#L996 It is passed like this: https://github.com/galchinsky/clay/blob/packGlobalState/compiler/evaluator.hpp#L53

Motivation: I faced with lack of modularity when tried to implement several things. The most important for me is implementation of JIT in compile-time. The idea is to run c code during compilation. This possibly allows to reduce the amount of primitives like staticError. This also could allow to get more access to the compiler from the program with external declarations. It can't be implemented with global variables. The first problem is JIT polluted global llvmModule. @jckarter notes that problem becomes worse if cross-compile: JIT needs to use llvm-target of host machine (i.e. loader, types etc. etc.), but everything is globally set to different target. My idea is to use different CompilerStates for host and target.

Sometimes I used tricks to avoid explicit passing. For example, in codegen the state is passed inside CodegenContext. The same for Env and Type. All tests have been passed.

jckarter commented 11 years ago

Good idea. I think it would be cleaner to make everything a method of a Compiler class than to add a parameter to every function, though.

galchinsky commented 11 years ago

There are several reasons.

I tried to use Analyzer, Codegen, Parser, Types and Loader classes, using pipl idiom (to wrap the .cpp code into impl not adding Blahblah:: everywhere). It worked good for Parser because it has small interface and a every function used global parser state. But it wasn't good for the other modules. It didn't decrease modularity because every class needs a reference to another. It increased verbosity because [this->]Analyzer is not shorter than cst-> even with impilcit this. Despite the only Compiler class is less verbose, but return everything in clay.hpp... I'm not sure that it is cleaner.

jckarter commented 11 years ago

Well, I don't think the code base is a great exemplar of good coding style as is, and I'd like it to better resemble other LLVM-based projects like Clang over time. I can see where you're coming from that retrofitting a class-like design is a lot more work, though.

I would say however that using reference-counted CompilerStatePtr etc. types is pointless, since the lifetime of the compiler state should always dominate that of the objects it uses, so it would be better to use plain pointers or references to CompilerState and use the non-refcounted llvm::OwningPtr to manage its lifetime from the toplevel.

galchinsky commented 11 years ago

Pointer<CompilerState> is my bad, let me fix it.

If I had a task to make the code more clang-ish I would:

  1. Extract global state (done)
  2. Extract this state to diffrenent contexts (not Analyzer+Codegen+Parser+Patterns+Lambdas+Types+Loader as I did, it is stupid and doesn't work)
  3. Convert this contexts to classes one by one. Each step is relatively small. The thing that stops me right now is that I don't know how to remix the code to reduce the amout of links between classes.

The third item is not as undoubted as the first two. C-compatibility is a good point. Current compiler interface seems to be very c-compatible. I thought it was done intend that's why I was talking about the code. Smartpointers are everywhere but they are intrusive. Inheritance is everywhere, but without VMT. Templates are not heavily used. Clone.cpp. It seems the compiler can be moved to .so library and accessed from everything with little changes. For example, from Clay. You know I'm going to add JIT in the compile time. This could allow access the compiler from Clay and write some code in it (bingen, repl, a part of primitives, desugaring, claydoc(?). The way like many compilers do bootstrapping: core language and the rest is written in this core. It definetly will be good when the syntax is frozen). Lastly clang guys have to maintain c-interface; if some man wants to add IDE-like stuff (e.g. emacs or sublime plugin) and the compiler has classes everywhere, he'll have to create c-interace too (or just drops this). So I don't think that C++ interfaces are undoubted win.