LLVM IR is a compiler IR
lists.cs.uiuc.edu
lists.cs.uiuc.edu
I like to read about LLVM and some of the things that are a bit (ok, in a lot of cases, way) above what I do day-to-day, except that it's sometimes taken for granted that the readers know the acronym/reference. I read the whole article and I understood what it was referring to, but for the life of me I couldn't land on the two words because IR in my world is always infrared.
Lesson learned : Make something people want.
I still think that LLVM is an incredibly great project, it brought compiler infrastructure to 2010s. But it is designed to be a static compiler framework, it hardly fits other purposes (think Unladen Swallow, don't know about Rubinius).
Currently, the LLVM JIT-powered OSL is now responsible for 100% of the shading duties at Sony Pictures Imageworks. I'd say that's a resounding success for LLVM as a JIT.
And, like the post said: LLVM IR is a compiler IR. It's a great fit here.
>> [LLVM] is designed to be a static compiler framework, it hardly fits other purposes [...]
I presented a ready example where LLVM was being used not as a "static compiler framework", and was being used for "other purposes". If that's not an outright refutation, it's at least a useful data point.
In addition, fast JIT speed was a very high priority/need and the OSL team spent a few months carefully tuning the LLVM passes to give fast code at a low JIT cost. (All of this was done and discussed publicly; feel free to grep the mailing list.)
Would you mind explaining how OSL is as close to something like Clang or GCC as you can get? I just don't see it.
JIT speed is relative. Shader programs are often executed many many many times, so OSL can likely afford to make different tradeoffs than many other JIT-using applications.
Using a static list of analyses that are always run before execution would be a static compiler approach, not a JIT approach. A JIT will generally profile the code, optimize it when it gets hot, and deoptimize it when assumptions made during optimization no longer hold (e.g. virtual calls in JVM being optimized to static calls because only one definition of a virtual method exists, but subsequently a new class is loaded that overrides that method). All this dynamic runtime modification of the code (not just initial compilation, but modification of existing, executing code) is where JIT technology is distinct from static compiler technology.
It seems that Open Shading Language is not really Just-in-Time (JIT) compiled. It's compiled from LLVM IR to native machine code Ahead-of-Time (AOT) but at runtime and then executed. This is actually not very far from a static compiler, the only difference is that the target architecture is known at runtime and the LLVM IR is compiled to machine code using that information.
What is the difference of JIT and AOT here is that in a JIT situation, there is some form of an interpreter that is executing byte code of some kind (probably not LLVM IR). When this interpreter reaches a loop of some kind, it will attempt to compile it (from bytecode to LLVM IR and finally from LLVM IR to native code). The interpreter then calls the compiled code, which will run for as long as possible and finally return control to the interpreter which will continue interpreting until it finds another opporturnity for JIT'ing. A JIT compiler is typically employed when the original source language cannot be compiled statically to machine code, because of e.g. dynamic typing.
So from what I can tell, OSL is just a static ahead of time compiler, with the final stage of compilation taking place at runtime.
Please tell me if some of my background facts were incorrect (in particular about OSL).
As opposed to something like, say, Google v8, which is using runtime feedback to make hot code paths fast (and to remove dynamism when it can be shown to be safe).
I guess I wasn't aware that people weren't including dynamic code generation and AOT compilation in the "JIT" category. To me JIT meant generating machine code "at runtime", and both OSL and v8 would be at opposite ends of the JIT "at runtime" code generation/compilation spectrum -- OSL on the AOT side and v8 on the keep-running-the-compiler side.
Actually I believe the bitcode is much larger than C or C++ binaries. The issue is that LLVM bitcode is basically a statically-typed language, lower than C. So for example to convert a pointer from one type to another you need to do an explicit cast (using an LLVM 'bitcast' operation). However, when you compile all the way down to native code, you have no need for such niceties and you just copy the value. So the bitcode ends up significantly larger than the native code would be, in order to maintain static typing correctness. There are some other issues as well.
LLVM bitcode was not designed for size, it's - exactly as the article says - just a compiler IR. So it isn't designed for size, portability, JITing speed, or anything like that. Trying to morph it into those is problematic and worse, may lead to compromises in LLVM's core goals. Which would be a shame since LLVM is a damn good compiler IR!
The LLVM IR format could just be the developer target so that things are actually portable, and we don't wind up in a situation where 90% of the NaCl plugins are x86-only.
Hi Dan,
I agree with almost all of the points you make, but not your conclusion. Many of the issues that you point out as problems are actually "features" that a VM like Java doesn't provide. For example, Java doesn't have uninitialized variables on the stack, and LLVM does. LLVM is capable of expressing the implicit zero initialization of variables that is implicit in Java, it just leaves the choice to the frontend.
Many of the other issues that you raise are true, but irrelevant when compared to other VMs. For example, LLVM allows a frontend to produce code that is ABI compatible with native C ABIs. It does this by requiring the frontend to know a lot about the native C ABI. Java doesn't permit this at all, and so LLVM having "this feature" seems like a feature over-and-above what high-level VMs provide. Similiarly, the "conditionally" supported features like large and obscurely sized integers simply don't exist in these VMs.
The one key feature that LLVM doesn't have that Java does, and which cannot be added to LLVM "through a small matter of implementation" is verifiable safety. Java bytecode verification is not something that LLVM IR permits, which you can't really do in LLVM (without resorting to techniques like SFI).
With all that said, I do think that we have a real issue here. The real issue is that we have people struggling to do things that a "hard" and see LLVM as the problem. For example:
1. The native client folks trying to use LLVM IR as a portable representation that abstracts arbitrary C calling conventions. This doesn't work because the frontend has to know the C calling conventions of the target.
2. The OpenCL folks trying to turn LLVM into a portable abstraction language by introducing endianness abstractions. This is hard because C is inherently a non-portable language, and this is only scratching the surface of the issues. To really fix this, OpenCL would have to be subset substantially, like the EFI C dialect.
> LLVM isn't actually a virtual machine. It's widely acknoledged that the > name "LLVM" is a historical artifact which doesn't reliably connote what > LLVM actually grew to be. LLVM IR is a compiler IR.
It sounds like you're picking a very specific definition of what a VM is. LLVM certainly isn't a high level virtual machine like Java, but that's exactly the feature that makes it a practical target for C-family languages. It isn't LLVM's fault that people want LLVM to magically solve all of C's portability problems.
-Chris
http://thread.gmane.org/gmane.comp.compilers.llvm.devel/4376...
Actually I'm not sure why this particular response was pointed out, as it is not perfect rebuttal. It's almost always better to even only skim whole thread than be picky about which mail to read carefully, as you'll always miss something then.
http://thread.gmane.org/gmane.comp.compilers.llvm.devel/4376...
Everybody seems to have a different (sometimes radically different) idea of what LLVM is for. That can't be good for making progress, and it's definitely not good for guys like me wondering if LLVM is a sane choice for a project.
Many corporations (most of all Apple) bet millions of $$$s in resources on projects that depend on LLVM, so don't worry too much.
I'm just a developer who has looked into LLVM in the recent past for possible use in cross-platform projects and come away more confused than enlightened, and this thread just magnified it. Interpreting that as "FUD" is wildly unhelpful.
LLVM initially looked like a candidate, but I walked away with conflicting ideas about whether it was even intended to be suitable for such a case, much less whether it actually was.
LLVM bitcode isn't a good intermediate format for platform independence, for transport or for interpretation. What it is good for is having separate compilation while delaying whole program optimization until "link time" (but is actually LLVM linking time, not actual object file linking). LLVM bitcode lets you have multiple bits and pieces of a program written in different languages yet still have whole program optimization (e.g. inlining) across language boundaries. Look at it like that.
If you're trying to create a JIT for multiple platforms, I'd start with a bytecode format that is either easy to interpret or easy to transform trivially into naive CPU instructions (the former is better for high-level optimizations because it will retain high-level semantics, the latter a better shortcut for speed by skipping interpretation); treat LLVM as a CPU target, but be aware you'll probably want to generate different LLVM instructions for minor differences between final target platforms that poke through the abstraction layer. You'll need to either invent that bytecode format yourself or reusing someone else's (depending on what language you're trying to execute, I expect); but I wouldn't look to LLVM to give you that bytecode.
Yes, the LLVM site does say it can be used in the construction of a virtual machine. That's factual. It has been used to construct actual, shipping virtual machines (e.g. Apple's MacRuby project). But that is not its primary or only use, and I don't think anyone would make that assumption if not for the name.
As for that thread, it contains some discussion among the developers about what LLVM is good for, but actually, nobody's really disagreeing with anybody else about what LLVM is and what it's good for.
Dan Gohman starts it out claiming that LLVM IR is not a very good bytecode for a portable virtual machine, and that if you want to build a portable VM, you'll need a separate layer for your bytecode.
Chris Lattner's reply (which you seem to read as disagrement) is essentially, "Yes, you're right, but here are the upsides that we get in exchange for those weaknesses as a bytecode."