[LLVMdev] LLVM IR is a compiler IR

233 views
Skip to first unread message

Dan Gohman

unread,
Oct 4, 2011, 2:53:18 PM10/4/11
to llvmdev@cs.uiuc.edu Mailing List
In this email, I argue that LLVM IR is a poor system for building a
Platform, by which I mean any system where LLVM IR would be a
format in which programs are stored or transmitted for subsequent
use on multiple underlying architectures.

LLVM IR initially seems like it would work well here. I myself was
once attracted to this idea. I was even motivated to put a bunch of
my own personal time into making some of LLVM's optimization passes
more robust in the absence of TargetData a while ago, even with no
specific project in mind. There are several things still missing,
but one could easily imagine that this is just a matter of people
writing some more code.

However, there are several ways in which LLVM IR differs from actual
platforms, both high-level VMs like Java or .NET and actual low-level
ISAs like x86 or ARM.

First, the boundaries of what capabilities LLVM provides are nebulous.
LLVM IR contains:

* Explicitly Target-specific features. These aren't secret;
x86_fp80's reason for being is pretty clear.

* Target-specific ABI code. In order to interoperate with native
C ABIs, LLVM requires front-ends to emit target-specific IR.
Pretty much everyone around here has run into this.

* Implicitly Target-specific features. The most obvious examples of
these are all the different Linkage kinds. These are all basically
just gateways to features in real linkers, and real linkers vary
quite a lot. LLVM has its own IR-level Linker, but it doesn't
do all the stuff that native linkers do.

* Target-specific limitations in seemingly portable features.
How big can the alignment be on an alloca? Or a GlobalVariable?
What's the widest supported integer type? LLVM's various backends
all have different answers to questions like these.

Even ignoring the fact that the quality of the backends in the
LLVM source tree varies widely, the question of "What can LLVM IR do?"
has numerous backend-specific facets. This can be problematic for
producers as well as consumers.

Second, and more fundamentally, LLVM IR is a fundamentally
vague language. It has:

* Undefined Behavior. LLVM is, at its heart, a C compiler, and
Undefined Behavior is one of its cornerstones.

High-level VMs typically raise predictable exceptions when they
encounter program errors. Physical machines typically document
their behavior very extensively. LLVM is fundamentally different
from both: it presents a bunch of rules to follow and then offers
no description of what happens if you break them.

LLVM's optimizers are built on the assumption that the rules
are never broken, so when rules do get broken, the code just
goes off the rails and runs into whatever happens to be in
the way. Sometimes it crashes loudly. Sometimes it silently
corrupts data and keeps running.

There are some tools that can help locate violations of the
rules. Valgrind is a very useful tool. But they can't find
everything. There are even some kinds of undefined behavior that
I've never heard anyone even propose a method of detection for.

* Intentional vagueness. There is a strong preference for defining
LLVM IR semantics intuitively rather than formally. This is quite
practical; formalizing a language is a lot of work, it reduces
future flexibility, and it tends to draw attention to troublesome
edge cases which could otherwise be largely ignored.

I've done work to try to formalize parts of LLVM IR, and the
results have been largely fruitless. I got bogged down in
edge cases that no one is interested in fixing.

* Floating-point arithmetic is not always consistent. Some backends
don't fully implement IEEE-754 arithmetic rules even without
-ffast-math and friends, to get better performance.

If you're familiar with "write once, debug everywhere" in Java,
consider the situation in LLVM IR, which is fundamentally opposed
to even trying to provide that level of consistency. And if you allow
the optimizer to do subtarget-specific optimizations, you increase
the chances that some bit of undefined behavior or vagueness will be
exposed.

Third, LLVM is a low level system that doesn't represent high-level
abstractions natively. It forces them to be chopped up into lots of
small low-level instructions.

* It makes LLVM's Interpreter really slow. The amount of work
performed by each instruction is relatively small, so the interpreter
has to execute a relatively large number of instructions to do simple
tasks, such as virtual method calls. Languages built for interpretation
do more with fewer instructions, and have lower per-instruction
overhead.

* Similarly, it makes really-fast JITing hard. LLVM is fast compared
to some other static C compilers, but it's not fast compared to
real JIT compilers. Compiling one LLVM IR level instruction at a
time can be relatively simple, ignoring the weird stuff, but this
approach generates comically bad code. Fixing this requires
recognizing patterns in groups of instructions, and then emitting
code for the patterns. This works, but it's more involved.

* Lowering high-level language features into low-level code locks
in implementation details. This is less severe in native code,
because a compiled blob is limited to a single hardware platform
as well. But a platform which advertizes architecture independence
which still has all the ABI lock-in of HLL implementation details
presents a much more frightening backwards compatibility specter.

* Apple has some LLVM IR transformations for Objective-C, however
the transformations have to reverse-engineer the high-level semantics
out of the lowered code, which is awkward. Further, they're
reasoning about high-level semantics in a way that isn't guaranteed
to be safe by LLVM IR rules alone. It works for the kinds of code
clang generates for Objective C, but it wouldn't necessarily be
correct if run on code produced by other front-ends. LLVM IR
isn't capable of representing the necessary semantics for this
unless we start embedding Objective C into it.


In conclusion, consider the task of writing an independent implementation
of an LLVM IR Platform. The set of capabilities it provides depends on who
you talk to. Semantic details are left to chance. There are features
which require a bunch of complicated infrastructure to implement which
are rarely used. And if you want light-weight execution, you'll
probably need to translate it into something else better suited for it
first. This all doesn't sound very appealing.

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.

Dan

_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

Talin

unread,
Oct 4, 2011, 4:23:20 PM10/4/11
to Dan Gohman, llvmdev@cs.uiuc.edu Mailing List
Thank you for writing this. First, I'd like to say that I am in 100% agreement with your points. I've been tempted many times to write something similar, although what you've written has been articulated much better than what I would have said.

When I try to explain to people what LLVM is I say "It's essentially the back-end of a compiler" - a job it does extremely well. I don't say "It's a virtual machine", because that is a job it doesn't do very well at all.

I'd like to add a couple of additional items to your list - first, LLVM IR isn't stable, and it isn't backwards compatible. Bitcode is not useful as an archival format, because a bitcode file cannot be loaded if it's even a few months out of sync with the code that loads it. Loading a bitcode file that is years old is hopeless.

Also, bitcode is large compared to Java or CLR bitcodes. This isn't such a big deal, but for people who want to ship code over the network it could be an issue.

I've been thinking that it would be a worthwhile project to develop a high-level IR that avoids many of the issues that you raise. Similar in concept to Java byte code, but without Java's limitations - for example it would support pass-by-value types. (CLR has this, but it also has limitations). Of course, this IR would of necessity be less flexible than LLVM IR, but you could always dip into IR where needed, such as C programs dip into assembly on occasion.

This hypothetical IR language would include a type system that was rich enough to express all of the DWARF semantics - so that instead of having two parallel representations of every type (one for LLVM's code generators and one for DWARF), you could instead generate both the LLVM types and the DWARF DI's from a common representation. This would have a huge savings in both complexity and the size of bitcode files.
--
-- Talin

James Molloy

unread,
Oct 4, 2011, 5:42:13 PM10/4/11
to Talin, Dan Gohman, llvmdev@cs.uiuc.edu Mailing List
Interestingly I wrote a bytecode language exactly like this for my master's thesis, based atop of LLVM. I abandoned the project after graduating, but it had it's promising moments.
________________________________________
From: llvmdev...@cs.uiuc.edu [llvmdev...@cs.uiuc.edu] On Behalf Of Talin [vir...@gmail.com]
Sent: 04 October 2011 21:23
To: Dan Gohman
Cc: llv...@cs.uiuc.edu Mailing List
Subject: Re: [LLVMdev] LLVM IR is a compiler IR

Thank you for writing this. First, I'd like to say that I am in 100% agreement with your points. I've been tempted many times to write something similar, although what you've written has been articulated much better than what I would have said.

When I try to explain to people what LLVM is I say "It's essentially the back-end of a compiler" - a job it does extremely well. I don't say "It's a virtual machine", because that is a job it doesn't do very well at all.

I'd like to add a couple of additional items to your list - first, LLVM IR isn't stable, and it isn't backwards compatible. Bitcode is not useful as an archival format, because a bitcode file cannot be loaded if it's even a few months out of sync with the code that loads it. Loading a bitcode file that is years old is hopeless.

Also, bitcode is large compared to Java or CLR bitcodes. This isn't such a big deal, but for people who want to ship code over the network it could be an issue.

I've been thinking that it would be a worthwhile project to develop a high-level IR that avoids many of the issues that you raise. Similar in concept to Java byte code, but without Java's limitations - for example it would support pass-by-value types. (CLR has this, but it also has limitations). Of course, this IR would of necessity be less flexible than LLVM IR, but you could always dip into IR where needed, such as C programs dip into assembly on occasion.

This hypothetical IR language would include a type system that was rich enough to express all of the DWARF semantics - so that instead of having two parallel representations of every type (one for LLVM's code generators and one for DWARF), you could instead generate both the LLVM types and the DWARF DI's from a common representation. This would have a huge savings in both complexity and the size of bitcode files.

Dan

LLV...@cs.uiuc.edu<mailto:LLV...@cs.uiuc.edu> http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

--
-- Talin

-- IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.

Renato Golin

unread,
Oct 4, 2011, 6:36:54 PM10/4/11
to Talin, llvmdev@cs.uiuc.edu Mailing List
Hi Talin,

I too agree 100% with Dan's words, and this could be a good pointer
for Jin-Gu Kang to continue on his pursuit for a better
target-independent bitcode.

Also, add your backwards compatibility issue to debug metadata in IR,
in which fields appear or disappear without notice.

But I think you hit a sweet spot here...


On 4 October 2011 21:23, Talin <vir...@gmail.com> wrote:
> This hypothetical IR language would include a type system that was rich
> enough to express all of the DWARF semantics - so that instead of having two
> parallel representations of every type (one for LLVM's code generators and
> one for DWARF), you could instead generate both the LLVM types and the DWARF
> DI's from a common representation. This would have a huge savings in both
> complexity and the size of bitcode files.

This is a really interesting idea. If you could describe your type
system in terms of Dwarf, you would have both: a rich type system AND
free Dwarf.

However, writing a back-end that would understand such a rich type
system AND language ABIs is out of the question.

We were discussing JIT and trying to come to a solution where JIT
wouldn't be as heavy as it has to be now, to no avail. Unless there is
a language that is of a higher level (like Java bytecode) or JIT will
always suffer.

If you join Dan's well said points, plus yours, Jin-Gu's and the
necessity of a decent JIT, it's almost reason enough to split the IR
into higher and lower versions (as proposed last year to deal with
complex type systems and ABIs).

Even some optimisations (maybe even Polly) could benefit from this
higher level representation, and all current optimisations can still
pass on the current, low-level, IR.

My tuppence.

cheers,
--renato

Jianzhou Zhao

unread,
Oct 4, 2011, 6:54:48 PM10/4/11
to Renato Golin, llvmdev@cs.uiuc.edu Mailing List
On Tue, Oct 4, 2011 at 6:36 PM, Renato Golin <reng...@systemcall.org> wrote:
> Hi Talin,
>
> I too agree 100% with Dan's words, and this could be a good pointer
> for Jin-Gu Kang to continue on his pursuit for a better
> target-independent bitcode.
>
> Also, add your backwards compatibility issue to debug metadata in IR,
> in which fields appear or disappear without notice.
>
> But I think you hit a sweet spot here...
>
>
> On 4 October 2011 21:23, Talin <vir...@gmail.com> wrote:
>> This hypothetical IR language would include a type system that was rich
>> enough to express all of the DWARF semantics - so that instead of having two
>> parallel representations of every type (one for LLVM's code generators and
>> one for DWARF), you could instead generate both the LLVM types and the DWARF
>> DI's from a common representation. This would have a huge savings in both
>> complexity and the size of bitcode files.
>
> This is a really interesting idea. If you could describe your type
> system in terms of Dwarf, you would have both: a rich type system AND
> free Dwarf.

This sounds interesting. I did not get what is a ``rich type system to
express all of the DWARF semantics''. Could you show an example
program that the rich type system can define, but the current IR fails
to present? And how does it improve the IR?

>
> However, writing a back-end that would understand such a rich type
> system AND language ABIs is out of the question.
>
> We were discussing JIT and trying to come to a solution where JIT
> wouldn't be as heavy as it has to be now, to no avail. Unless there is
> a language that is of a higher level (like Java bytecode) or JIT will
> always suffer.
>
> If you join Dan's well said points, plus yours, Jin-Gu's and the
> necessity of a decent JIT, it's almost reason enough to split the IR
> into higher and lower versions (as proposed last year to deal with
> complex type systems and ABIs).
>
> Even some optimisations (maybe even Polly) could benefit from this
> higher level representation, and all current optimisations can still
> pass on the current, low-level, IR.
>
> My tuppence.
>
> cheers,
> --renato
> _______________________________________________
> LLVM Developers mailing list
> LLV...@cs.uiuc.edu         http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>

--
Jianzhou

Renato Golin

unread,
Oct 4, 2011, 7:06:54 PM10/4/11
to Jianzhou Zhao, llvmdev@cs.uiuc.edu Mailing List
On 4 October 2011 23:54, Jianzhou Zhao <jian...@seas.upenn.edu> wrote:
> This sounds interesting. I did not get what is a ``rich type system to
> express all of the DWARF semantics''. Could you show an example
> program that the rich type system can define, but the current IR fails
> to present? And how does it improve the IR?

Any code with C++ classes, C unions and bit-fields would be much
improved. Talin might help you with non-C++ types.

Basically anything that has to be kludged to be lowered to IR would
benefit from a higher-level IR. ByValue calls, self pointers, RTTI,
virtual inheritance, multiple inheritance.

Dan's argument that IR is unstable is clear when you get to write a
front-end from scratch. The first front-end generated a lot of kludge
to lower C++, since it'd take years to implement it properly in IR
(and all back-ends). The second, third and so on were forced to follow
the same kludge. Non-C++ front-ends suffer even more, since they have
to kludge their languages into a C++-semantic kludge, which has no
1-to-1 relationship with the original semantics of their code.

Search on the list about the topics above and you'll see that there
was a lot of discussion wasted on them for years and years.


--
cheers,
--renato

http://systemcall.org/

Chris Lattner

unread,
Oct 4, 2011, 7:19:54 PM10/4/11
to Dan Gohman, llvmdev@cs.uiuc.edu Mailing List
On Oct 4, 2011, at 11:53 AM, Dan Gohman wrote:
> In this email, I argue that LLVM IR is a poor system for building a
> Platform, by which I mean any system where LLVM IR would be a
> format in which programs are stored or transmitted for subsequent
> use on multiple underlying architectures.

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

Talin

unread,
Oct 4, 2011, 7:31:48 PM10/4/11
to Jianzhou Zhao, llvmdev@cs.uiuc.edu Mailing List
On Tue, Oct 4, 2011 at 3:54 PM, Jianzhou Zhao <jian...@seas.upenn.edu> wrote:
On Tue, Oct 4, 2011 at 6:36 PM, Renato Golin <reng...@systemcall.org> wrote:
> Hi Talin,
>
> I too agree 100% with Dan's words, and this could be a good pointer
> for Jin-Gu Kang to continue on his pursuit for a better
> target-independent bitcode.
>
> Also, add your backwards compatibility issue to debug metadata in IR,
> in which fields appear or disappear without notice.
>
> But I think you hit a sweet spot here...
>
>
> On 4 October 2011 21:23, Talin <vir...@gmail.com> wrote:
>> This hypothetical IR language would include a type system that was rich
>> enough to express all of the DWARF semantics - so that instead of having two
>> parallel representations of every type (one for LLVM's code generators and
>> one for DWARF), you could instead generate both the LLVM types and the DWARF
>> DI's from a common representation. This would have a huge savings in both
>> complexity and the size of bitcode files.
>
> This is a really interesting idea. If you could describe your type
> system in terms of Dwarf, you would have both: a rich type system AND
> free Dwarf.

This sounds interesting. I did not get what is a ``rich type system to
express all of the DWARF semantics''. Could you show an example
program that the rich type system can define, but the current IR fails
to present? And how does it improve the IR?

One thing you would need is the ability to assign names to struct members. Currently LLVM refers to struct members by numerical index, and I wouldn't want to change that. However, in order to be visible in the debugger, you also have to assign a name to each member. Note that this information doesn't need to take a lot of space in the module - names are highly compressible, especially fully qualified names (foo.bar.X.Y.Z) where you have a whole bunch of names that start with the same prefix. (In my own frontend, I sort names by frequency, so that the names that are used most often have the lowest assigned IDs. This allows the reference to the name to be stored in fewer bits.)

Similarly, in order to handle inheritance, you would need a way to indicate which fields of the struct were "inherited". Currently, inheritance is handled by embedding the parent class as the first member of the child class - but the IR level can't tell whether that first member is inherited or is just a regular member.

Both of these are features that LLVM IR doesn't need, but which would be nice to have in a higher-level IR based on top of LLVM.

Note that you could also use the "rich type system" for generating reflection data as well. So that's a third use case.

I better stop right now before I convince myself to do something crazy, like write my own VM.

>
> However, writing a back-end that would understand such a rich type
> system AND language ABIs is out of the question.
>
> We were discussing JIT and trying to come to a solution where JIT
> wouldn't be as heavy as it has to be now, to no avail. Unless there is
> a language that is of a higher level (like Java bytecode) or JIT will
> always suffer.
>
> If you join Dan's well said points, plus yours, Jin-Gu's and the
> necessity of a decent JIT, it's almost reason enough to split the IR
> into higher and lower versions (as proposed last year to deal with
> complex type systems and ABIs).
>
> Even some optimisations (maybe even Polly) could benefit from this
> higher level representation, and all current optimisations can still
> pass on the current, low-level, IR.
>
> My tuppence.
>
> cheers,
> --renato
> _______________________________________________
> LLVM Developers mailing list
> LLV...@cs.uiuc.edu         http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
>



--
Jianzhou



--
-- Talin

Chris Lattner

unread,
Oct 4, 2011, 7:36:35 PM10/4/11
to Talin, llvmdev@cs.uiuc.edu Mailing List

On Oct 4, 2011, at 4:31 PM, Talin wrote:


This sounds interesting. I did not get what is a ``rich type system to
express all of the DWARF semantics''. Could you show an example
program that the rich type system can define, but the current IR fails
to present? And how does it improve the IR?

One thing you would need is the ability to assign names to struct members. Currently LLVM refers to struct members by numerical index, and I wouldn't want to change that. However, in order to be visible in the debugger, you also have to assign a name to each member. Note that this information doesn't need to take a lot of space in the module - names are highly compressible, especially fully qualified names (foo.bar.X.Y.Z) where you have a whole bunch of names that start with the same prefix. (In my own frontend, I sort names by frequency, so that the names that are used most often have the lowest assigned IDs. This allows the reference to the name to be stored in fewer bits.)

Talin, if you're trying to produce a "C virtual machine" you basically need everything in a C AST.  You're scratching the surface of the issues.

-Chris

Renato Golin

unread,
Oct 4, 2011, 7:42:07 PM10/4/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
On 5 October 2011 00:19, Chris Lattner <clat...@apple.com> wrote:
> 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.
(...)

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

This is a very simplistic point of view, and TBH, I'm a bit shocked.

Having a "nicer codebase" and "friendlier community" are two strong
points for LLVM against GCC, but they're too weak to migrate people
from GCC to LLVM.

JIT, "the native client folks", "the openCL folks" are showing how
powerful LLVM could be, if it was a bit more accommodating. Without
those troublesome folks, LLVM is just another compiler, like GCC, and
being completely blunt, it's no better.

The infrastructure to add new passes is better, but the number and
quality of passes is not. It's way easier to create new back-ends, but
the existing number and quality, again, no better. The "good code" is
suffering a diverse community, large codebase and company's interests,
which is not a good forecast for code quality. It's not just the IR
that has a lot of kludge, back-ends, front-ends, dwarf emitter,
exception handling, etc., although some nicer than GCC, it's not
complete nor accurate.

If you want to bet on a "fun community" to drive LLVM, I don't think
you'll go too far. And if you want to discard the OpenCL, JIT and
NativeClient-style community, well, there won't be much of a community
to be any fun...

If you want to win on the code quality battle, while working for a big
company, good luck. Part of the GCC community's grunts are towards
companies trying to push selfish code in, and well, their reasons are
not all without merit.

I didn't see people looking for a magic wand on these discussions so far...


--
cheers,
--renato

http://systemcall.org/

_______________________________________________

Talin

unread,
Oct 4, 2011, 7:41:55 PM10/4/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
I was trying to limit my response to just a few examples - I realize that there's a ton of things I've left out. I guess what I am describing is the same as Microsoft's "Managed C++" - the ability to compile C++ programs to the CLR. Note that Managed C++ doesn't give you every possible feature of C++, you can't take an arbitrary C++ program and expect to compile it in managed mode.
 
-Chris



--
-- Talin

Talin

unread,
Oct 4, 2011, 7:56:20 PM10/4/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
I understand that the official goals of the LLVM project are carefully limited. A large number of LLVM users are perfectly happy to live within the envelope of what LLVM provides. At the same time, there are also a fair number of users who are aiming for things that appear to be just outside that envelope. These "near miss" users are looking at Java, at CLR, and constantly asking themselves "did I make the right decision betting on LLVM rather than these other platforms?" Unfortunately, there are frustratingly few choices available in this space, and LLVM happens to be "nearest" conceptually to what these users want to accomplish. But bridging the gap between where they want to go and where LLVM is headed is often quite a challenge, one that is measured in multiple man-years of effort.

-Chris

_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

--
-- Talin

Chris Lattner

unread,
Oct 4, 2011, 8:08:54 PM10/4/11
to Talin, llvmdev@cs.uiuc.edu Mailing List

On Oct 4, 2011, at 4:56 PM, Talin wrote:

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

I understand that the official goals of the LLVM project are carefully limited. A large number of LLVM users are perfectly happy to live within the envelope of what LLVM provides. At the same time, there are also a fair number of users who are aiming for things that appear to be just outside that envelope. These "near miss" users are looking at Java, at CLR, and constantly asking themselves "did I make the right decision betting on LLVM rather than these other platforms?" Unfortunately, there are frustratingly few choices available in this space, and LLVM happens to be "nearest" conceptually to what these users want to accomplish. But bridging the gap between where they want to go and where LLVM is headed is often quite a challenge, one that is measured in multiple man-years of effort.

I completely agree, and I'm really interested in LLVM improving to solve these sorts of problems.  I'm not sure how this relates to Dan's email or my response though.

-Chris

Chris Lattner

unread,
Oct 4, 2011, 8:07:56 PM10/4/11
to Talin, llvmdev@cs.uiuc.edu Mailing List

On Oct 4, 2011, at 4:41 PM, Talin wrote:

One thing you would need is the ability to assign names to struct members. Currently LLVM refers to struct members by numerical index, and I wouldn't want to change that. However, in order to be visible in the debugger, you also have to assign a name to each member. Note that this information doesn't need to take a lot of space in the module - names are highly compressible, especially fully qualified names (foo.bar.X.Y.Z) where you have a whole bunch of names that start with the same prefix. (In my own frontend, I sort names by frequency, so that the names that are used most often have the lowest assigned IDs. This allows the reference to the name to be stored in fewer bits.)

Talin, if you're trying to produce a "C virtual machine" you basically need everything in a C AST.  You're scratching the surface of the issues.

I was trying to limit my response to just a few examples - I realize that there's a ton of things I've left out. I guess what I am describing is the same as Microsoft's "Managed C++" - the ability to compile C++ programs to the CLR. Note that Managed C++ doesn't give you every possible feature of C++, you can't take an arbitrary C++ program and expect to compile it in managed mode.

Are you willing to give up the ability to interoperate with existing ABIs and code compiled by other compilers?

-Chris

Chris Lattner

unread,
Oct 4, 2011, 8:19:25 PM10/4/11
to Renato Golin, llvmdev@cs.uiuc.edu Mailing List
On Oct 4, 2011, at 4:42 PM, Renato Golin wrote:
> On 5 October 2011 00:19, Chris Lattner <clat...@apple.com> wrote:
>> 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.
> (...)
>> 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,
>
> This is a very simplistic point of view, and TBH, I'm a bit shocked.

I'm sorry, I didn't mean to be offensive.

> JIT, "the native client folks", "the openCL folks" are showing how
> powerful LLVM could be, if it was a bit more accommodating. Without
> those troublesome folks, LLVM is just another compiler, like GCC, and
> being completely blunt, it's no better.

I'm not sure what you're getting at here. My email was not intended to say that I'm not interested in LLVM improving - quite the contrary. My email was to rebut Dan's implicit claim that PNaCL and using LLVM as a portable IR is never going to work. I'm arguing in the "opencl" and "pnacl" folks favor :)

That said, I'm trying to also inject realism. C is an inherently hostile language to try to get portability out of.

> The infrastructure to add new passes is better, but the number and
> quality of passes is not. It's way easier to create new back-ends, but
> the existing number and quality, again, no better. The "good code" is
> suffering a diverse community, large codebase and company's interests,
> which is not a good forecast for code quality. It's not just the IR
> that has a lot of kludge, back-ends, front-ends, dwarf emitter,
> exception handling, etc., although some nicer than GCC, it's not
> complete nor accurate.

I'm not sure what point you're trying to make here. We all know that LLVM sucks, fortunately lots of people seem to be motivated to help make it better :)

> If you want to bet on a "fun community" to drive LLVM, I don't think
> you'll go too far. And if you want to discard the OpenCL, JIT and
> NativeClient-style community, well, there won't be much of a community
> to be any fun...

I think that we're pretty strongly miscommunicating...

-Chris

David Sehr

unread,
Oct 4, 2011, 8:39:00 PM10/4/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
All,

I should have chimed in earlier, but have been working on two more side-channel variants of this conversation.
At the beginning the PNaCl team was strongly pushing for trying to keep platform ABI compatibility on all
platforms while taking one portable bitcode stream as input.  During the discussions we've had over the past few
weeks it became obvious that that is simply not tractable, and we're trying to work in a slightly different direction
that meets PNaCl's needs.

PNaCl needs to have one bitcode representation that can be lowered to efficient ABIs on all target platforms.
We're not constrained to have to use the exact ABI that trusted code would on each of the platforms, however.
For reasons of portability we've already eliminated long double support (or made it equivalent to double if you prefer).
And we're currently proposing to use the platform ABIs, except that structure arguments (including unions, bitfields, etc.)
are always passed in memory.  With this set of caveats we think we can meet our objectives for at least x86-32,
x86-64, and ARM.  The remaining complexity is making byval usable on all platforms so that we can have one representation.
Of course we'd like community input on the issues we've overlooked.

There are other issues of compatibility that still keep us awake at night, of course, and I hope the next developers'
conference will give us the chance to wrangle with some of those.

David

Óscar Fuentes

unread,
Oct 4, 2011, 8:48:43 PM10/4/11
to llv...@cs.uiuc.edu
Dan Gohman <goh...@apple.com> writes:

Great post, Dan. Some comments follow.

[snip]

> * Target-specific ABI code. In order to interoperate with native
> C ABIs, LLVM requires front-ends to emit target-specific IR.
> Pretty much everyone around here has run into this.

There are places where compatibility with the native C ABI is taken too
far. For instance, time ago I noted that what the user sets through
Module::setDataLayout is simply ignored. LLVM uses the data layout
required by the native C ABI, which is hardcoded into LLVM's source
code. So I asked: pass the value setted by Module::setDataLayout to the
layers that are interested on it, as any user would expect. The response
I got was, in essence, "As you are not working on C/C++, I couldn't care
less about your language's requirements." So I have a two-line patch on
my LLVM local copy, which has the effect of making the IR code generated
by my compiler portable across Linux/x86 and Windows/x86 (although that
was not the reason I wanted the change.)

So it is true that LLVM IR has portability limitations, but not all of
them are intrinsic to the LLVM IR nature.

[snip]

Talin

unread,
Oct 5, 2011, 1:15:02 AM10/5/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
For my own needs, I'd be willing to live with a system that requires extra effort on behalf of the programmer (in the form of __attributes or #pragmas or SWIG) when calling out to code produced by other compilers. In other words, I'd like for the kind of interoperability you describe to be *possible*, but I don't necessarily require or expect that it be *easy*.

-- Talin

Talin

unread,
Oct 5, 2011, 1:29:58 AM10/5/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
Here's my position in a nutshell: The kind of things that Dan wants LLVM to do should really be a separate sub-project from LLVM proper, built on top of LLVM. I think it's unrealistic to expect LLVM proper to adopt Dan's stated objectives - but at the same time, it would be a awful shame if there wasn't something that could meet his needs, since I think many people other than Dan would benefit from such a thing.

For example, I don't expect that LLVM IR should suddenly become stable and usable as an archive format, but I think it entirely reasonable that someone could come up with a higher-level IR, translatable into LLVM IR, that does have those qualities. The fact that we have projects that convert JVM bytecode into LLVM IR is proof that such a thing is possible. (Except that any language implementer willing to live with the limitations of the JVM probably wouldn't be on this mailing list to begin with, but I digress.)

The question I am interested in exploring is whether the goals of the "near miss" users of LLVM are similar enough to each other to be worth having a conversation about how to achieve those goals collaboratively, or whether it is better that we should each continue to struggle with our own problems individually.

--
-- Talin

John McCall

unread,
Oct 5, 2011, 1:50:59 AM10/5/11
to David Sehr, llvmdev@cs.uiuc.edu Mailing List
On Oct 4, 2011, at 5:39 PM, David Sehr wrote:
> I should have chimed in earlier, but have been working on two more side-channel variants of this conversation.
> At the beginning the PNaCl team was strongly pushing for trying to keep platform ABI compatibility on all
> platforms while taking one portable bitcode stream as input. During the discussions we've had over the past few
> weeks it became obvious that that is simply not tractable, and we're trying to work in a slightly different direction
> that meets PNaCl's needs.
>
> PNaCl needs to have one bitcode representation that can be lowered to efficient ABIs on all target platforms.
> We're not constrained to have to use the exact ABI that trusted code would on each of the platforms, however.
> For reasons of portability we've already eliminated long double support (or made it equivalent to double if you prefer).
> And we're currently proposing to use the platform ABIs, except that structure arguments (including unions, bitfields, etc.)
> are always passed in memory.

Excellent! Assuming that PNaCl has settled on a standard object layout — obviously a hard requirement in any case — this seems like a solid path towards portability.

John.

Jin Gu Kang

unread,
Oct 5, 2011, 2:48:00 AM10/5/11
to vir...@gmail.com, llv...@cs.uiuc.edu
Hi Talin,
 
I had a talk in London developer meeting about More Target Independent Bitcode.
(You can download the slides from http://llvm.org/devmtg/2011-09-16/)
 
I have been also trying to make bitcode with more higher abstraction level.
I made new compilation strategy using more target indepent bitcode as followins,
(This didn't consider about Jit or Interpreter like lli)
 
C/C++ source code
------------------------------------------ using front-end complier
Target Independent Bitcode
------------------------------------------ using translator
Traget Dependent Bitcode
------------------------------------------ using opt with optimization passes
Optimized Target Dependent Bitcode
------------------------------------------ using LLC
Target Assembly code
 
I can show you simple example with this strategy.
 
C/C++ source code
  1 struct foo {
  2 char a:4;
  3 long long b:61;
  4 int c:30;
  5 };
  6
  7 struct foo var;
Target Independent Bitcode
  1 ; ModuleID = 'gu.c'
  2 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32
-f64:64:64-v64:64:64-v128:128:128-a0:0:64-f192:32:32"
  3 target triple = "ovm-none-linux"
  4
  5 %struct.foo = type { i4(char), i61(longlong), i30(int) }
  6
  7 @var = common global %struct.foo zeroinitializer  ; <%struct.foo*> [#uses=0]
Target Dependent Bitcode
ARM
  1 ; ModuleID = 'gu.mod.opt.arm.bc'
  2 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32
-f64:64:64-v64:64:64-v128:128:128-a0:0:64"
  3 target triple = "armv5-none-linux-gnueabi"
  4
  5 %struct.foo = type <{ i8, [7 x i8], i64, i32, [4 x i8] }>
  6
  7 @var = common global %struct.foo zeroinitializer  ; <%struct.foo*> [#uses=0]
X86
  1 ; ModuleID = 'gu.mod.opt.x86.bc'
  2 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32
-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32"
  3 target triple = "i386-pc-linux-gnu"
  4
  5 %struct.foo = type <{ i8, [3 x i8], i64, i32 }>
  6
  7 @var = common global %struct.foo zeroinitializer  ; <%struct.foo*> [#uses=0]

 

I have inserted additional information into bitcode and have modified some codes of LLVM.

 

I am interested in having conversation about achieving the goals of the "near miss" users

of LLVM collaboratively. :)

 

Thanks,

Jin-Gu Kang

Jin Gu Kang

unread,
Oct 5, 2011, 3:04:42 AM10/5/11
to David Sehr, Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
Hi David,
 
I have been also trying to make one bitcode representation that
can be lowered to efficient ABIs on all target platforms.
 
I made new compilation strategy to achieve this goal as follows,
 
C/C++ source code
------------------------------------------ using front-end complier
Target Independent Bitcode
------------------------------------------ using translator
Traget Dependent Bitcode
------------------------------------------ using opt with optimization passes
Optimized Target Dependent Bitcode
------------------------------------------ using LLC
Target Assembly code
 
I can show you simple example with this strategy.
 
C/C++ source code
  1 #include <stdio.h>
  2
  3 int main(void) {
  4   long double a = 3.14;
  5   long double b = 2.2;
  6   long double c;
  7
  8   c = a + b;
  9
 10   printf("c=%Lf\n", c);
 11   return 0;
 12 }
------------------------------------------------------------------------------------------
Target Independent Bitcode
  1 ; ModuleID = 'long_double.c'

  2 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64   
 -v64:64:64-v128:128:128-a0:0:64-f192:32:32"
  3 target triple = "ovm-none-linux"
  4
  5 @.str = private constant [7 x i8(char)] [i8(char) 99, i8(char) 61, i8(char) 37, i8(char) 76, i8(c
    har) 102, i8(char) 10, i8(char) 0], align 1 ; <[7 x i8(char)]*> [#uses=1]
  6
  7 define i32(int) @main() nounwind {
  8 entry:
  9   %retval = alloca i32(int)                       ; <i32(int)*> [#uses=2]
 10   %c = alloca long_double                         ; <long_double*> [#uses=2]
 11   %b = alloca long_double                         ; <long_double*> [#uses=2]
 12   %a = alloca long_double                         ; <long_double*> [#uses=2]
 13   %0 = alloca i32(int)                            ; <i32(int)*> [#uses=2]
 14   %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
 15   store long_double 0xL00000000000000810000000000000000C8F5C28F5C28F800, long_double* %a, align 8
 16   store long_double 0xL000000000000008100000000000000008CCCCCCCCCCCD000, long_double* %b, align 8
 17   %1 = load long_double* %a, align 8              ; <long_double> [#uses=1]
 18   %2 = load long_double* %b, align 8              ; <long_double> [#uses=1]
 19   %3 = fadd long_double %1, %2                    ; <long_double> [#uses=1]
 20   store long_double %3, long_double* %c, align 8
 21   %4 = load long_double* %c, align 8              ; <long_double> [#uses=1]
 22   %5 = call i32(int) (i8(char)*, ...)* @printf(i8(char)* noalias getelementptr inbounds ([7 x i8(
    char)]* @.str, i32(int) 0, i32(int) 0), long_double %4) nounwind ; <i32(int)> [#uses=0]
 23   store i32(int) 0, i32(int)* %0, align 4
 24   %6 = load i32(int)* %0, align 4                 ; <i32(int)> [#uses=1]
 25   store i32(int) %6, i32(int)* %retval, align 4
 26   br label %return
 27
 28 return:                                           ; preds = %entry
 29   %retval1 = load i32(int)* %retval               ; <i32(int)> [#uses=1]
 30   ret i32(int) %retval1
 31 }
 32
 33 declare i32(int) @printf(i8(char)* noalias, ...) nounwind
------------------------------------------------------------------------------------------
Target Dependent Bitcode
ARM
  1 ; ModuleID = 'long_double.mod.opt.arm.bc'

  2 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64   
 -v64:64:64-v128:128:128-a0:0:64"
  3 target triple = "armv5-none-linux-gnueabi"
  4
  5 @.str = private constant [7 x i8] c"c=%Lf\0A\00", align 1 ; <[7 x i8]*> [#uses=1]
  6
  7 define i32 @main() nounwind {
  8 entry:
  9   %retval = alloca i32                            ; <i32*> [#uses=2]
 10   %c = alloca double                              ; <double*> [#uses=2]
 11   %b = alloca double                              ; <double*> [#uses=2]
 12   %a = alloca double                              ; <double*> [#uses=2]
 13   %0 = alloca i32                                 ; <i32*> [#uses=2]
 14   %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
 15   store double 3.140000e+00, double* %a, align 8
 16   store double 2.200000e+00, double* %b, align 8
 17   %1 = load double* %a, align 8                   ; <double> [#uses=1]
 18   %2 = load double* %b, align 8                   ; <double> [#uses=1]
 19   %3 = fadd double %1, %2                         ; <double> [#uses=1]
 20   store double %3, double* %c, align 8
 21   %4 = load double* %c, align 8                   ; <double> [#uses=1]
 22   %5 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([7 x i8]* @.str, i32 0, i 
   32 0), double %4) nounwind ; <i32> [#uses=0]
 23   store i32 0, i32* %0, align 4
 24   %6 = load i32* %0, align 4                      ; <i32> [#uses=1]
 25   store i32 %6, i32* %retval, align 4
 26   br label %return
 27
 28 return:                                           ; preds = %entry
 29   %retval1 = load i32* %retval, align 4           ; <i32> [#uses=1]
 30   ret i32 %retval1
 31 }
 32
 33 declare i32 @printf(i8* noalias, ...) nounwind
X86
  1 ; ModuleID = 'long_double.mod.opt.x86.bc'

  2 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64
    -v64:64:64-v128:128:128-a0:0:64-f80:32:32"
  3 target triple = "i386-pc-linux-gnu"
  4
  5 @.str = private constant [7 x i8] c"c=%Lf\0A\00", align 1 ; <[7 x i8]*> [#uses=1]
  6
  7 define i32 @main() nounwind {
  8 entry:
  9   %retval = alloca i32                            ; <i32*> [#uses=2]
 10   %c = alloca x86_fp80                            ; <x86_fp80*> [#uses=2]
 11   %b = alloca x86_fp80                            ; <x86_fp80*> [#uses=2]
 12   %a = alloca x86_fp80                            ; <x86_fp80*> [#uses=2]
 13   %0 = alloca i32                                 ; <i32*> [#uses=2]
 14   %"alloca point" = bitcast i32 0 to i32          ; <i32> [#uses=0]
 15   store x86_fp80 0xK4000C8F5C28F5C28F800, x86_fp80* %a, align 4
 16   store x86_fp80 0xK40008CCCCCCCCCCCD000, x86_fp80* %b, align 4
 17   %1 = load x86_fp80* %a, align 4                 ; <x86_fp80> [#uses=1]
 18   %2 = load x86_fp80* %b, align 4                 ; <x86_fp80> [#uses=1]
 19   %3 = fadd x86_fp80 %1, %2                       ; <x86_fp80> [#uses=1]
 20   store x86_fp80 %3, x86_fp80* %c, align 4
 21   %4 = load x86_fp80* %c, align 4                 ; <x86_fp80> [#uses=1]
 22   %5 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([7 x i8]* @.str, i32 0, i 
   32 0), x86_fp80 %4) nounwind ; <i32> [#uses=0]
 23   store i32 0, i32* %0, align 4
 24   %6 = load i32* %0, align 4                      ; <i32> [#uses=1]
 25   store i32 %6, i32* %retval, align 4
 26   br label %return
 27
 28 return:                                           ; preds = %entry
 29   %retval1 = load i32* %retval, align 4           ; <i32> [#uses=1]
 30   ret i32 %retval1
 31 }
 32
 33 declare i32 @printf(i8* noalias, ...) nounwind
I made new common long double type and inserted it into bitcode.
 
As Talin mentioned,

Duncan Sands

unread,
Oct 5, 2011, 3:37:34 AM10/5/11
to llv...@cs.uiuc.edu
Hi Talin,

> I'd like to add a couple of additional items to your list - first, LLVM IR isn't
> stable, and it isn't backwards compatible. Bitcode is not useful as an archival
> format, because a bitcode file cannot be loaded if it's even a few months out of
> sync with the code that loads it. Loading a bitcode file that is years old is
> hopeless.

that sounds like a bug, assuming the bitcode was produced by released versions
of LLVM (bitcode produced with some intermediate development version of LLVM may
or may not be loadable in the final release). Maybe you should open some bug
reports?

Ciao, Duncan.

Renato Golin

unread,
Oct 5, 2011, 4:17:30 AM10/5/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
On 5 October 2011 01:19, Chris Lattner <clat...@apple.com> wrote:
> I'm not sure what you're getting at here.  My email was not intended to say that I'm not interested in LLVM improving - quite the contrary.  My email was to rebut Dan's implicit claim that PNaCL and using LLVM as a portable IR is never going to work.  I'm arguing in the "opencl" and "pnacl" folks favor :)

Hi Chris,

Ok, I think I (now) get your drift. Let's restart the conversation, then. ;)

If I got it right this time, from your point of view, Dan's arguments
are not accurate because IR never intended to be anything else anyway.
PNaCl, OpenCL, RenderScript, Java-like VMs were aggregated over time
and tried to use IR for what it was not designed to be. I completely
agree with you in that one.

If I'm not mistaken, JIT was never intended to be portable, but to
test IR during a time back-ends were not stable enough. IR was never
intended to cover ABI issues, complex type system, endianness of the
read/write, etc. In effect, IR was never intended to be completely
language/target agnostic. Again, I completely agree on that one.

But there is a subliminal context that I don't think people
understand, and that was my point.

Communities are powerful things. It takes a while to create, but once
you have it, it has a life of its own. For the community, it doesn't
matter much what were the original goals of LLVM, just what you can do
with it, now. LLVM is the *only* compilation infrastructure I know
that is flexible and extensible enough to allow people to do such
radical things in radically different ways.

In a nutshell, Chris, you are a victim of your own success. If LLVM
wasn't that flexible, people wouldn't stretch it that much, and you
wouldn't have those problems.

LLVM's community is strong, active and passionate. But OpenCL folks
will be passionate about adding OpenCL features, and so on and that
creates tension (and I understand the defensive position you have
always been, protecting LLVM's integrity).

But if you read in between the lines of what people are saying (and
what I heard over and over during the Euro-LLVM), people are skeptical
of the portability issue. Almost everyone, including experienced
compiler engineers, comes to LLVM thinking the IR is portable. So,
either we're all doing the wrong advertising of what LLVM really is,
or we're not doing our jobs right.

I'm now reading David Sehr's answer (and John's reply) and I think
we're still missing the point. David listed the hacks he had to do to
make it somewhat portable. I've listed (mostly last year) the hacks we
had to do to implement the EDG bridge. Talin keeps reminding us what
he has to do to work on his compiler. James wrote his own bytecode
language on top of IR.

If LLVM IR is not portable, and never will be, that's fine. I can live
with that. But ignoring the crowd is not wise. I'm not saying you guys
should implement a higher-level IR or change the current IR to fit to
everyone's needs, that would be madness. What I'm asking is simply
that you stop ignoring the fact that there is a problem (that lots of
people want more portable IR) and that the solution is NOT to kludge
it into the current IR.

When people show hacks, don't say "excellent! problem solved". When
people ask for portability solutions, don't recommend kludges, or "do
like clang". Encourage people to contribute to portability, and be
willing to accept such contributions even if that generates a bit more
work to get it through than a kludge.

--
cheers,
--renato

http://systemcall.org/


PS: I hope to have gotten it right this time, neither were meant as rants...

Duncan Sands

unread,
Oct 5, 2011, 4:53:14 AM10/5/11
to llv...@cs.uiuc.edu
Hi Óscar,

> There are places where compatibility with the native C ABI is taken too
> far. For instance, time ago I noted that what the user sets through
> Module::setDataLayout is simply ignored.

it's not ignored, it's used by the IR level optimizers. That way these
optimizers can know stuff about the target without having to be linked
to a target backend.

LLVM uses the data layout
> required by the native C ABI, which is hardcoded into LLVM's source
> code. So I asked: pass the value setted by Module::setDataLayout to the
> layers that are interested on it, as any user would expect.

There are two classes of information in datalayout: things which correspond
to stuff hard-wired into the target processor (for example that x86 is little
endian), and stuff which is not hard-wired in (for example the alignment of
x86 long double, which is 4 or 8 bytes on x86-32 depending on whether you are
on linux, darwin or windows). Hoping to have code generators override the
hard-wired stuff if they see something different in the data layout is just
too much to ask for - eg the x86 code generators are never going to produce big
endian code just because you set big-endianness in the datalayout. Even the
second class of "soft" parameters is not completely flexible: for example most
processors enforce a minimum alignment for types, and trying to reduce it by
giving types a lesser alignment in the datalayout just isn't going to work.
So given that the ways in which codegen could adapt to various datalayout
settings are quite limited and constrained by the target, does it really make
sense to try to parametrize the codegenerators by the datalayout at all?
In any case, it might be good if the code generators produced a warning if they
see that the datalayout string doesn't correspond to what codegen thinks it
should be (I though someone added that already?).

Ciao, Duncan.

Óscar Fuentes

unread,
Oct 5, 2011, 7:32:45 AM10/5/11
to llv...@cs.uiuc.edu
Hello Dan.

Duncan Sands <bald...@free.fr> writes:

>> There are places where compatibility with the native C ABI is taken too
>> far. For instance, time ago I noted that what the user sets through
>> Module::setDataLayout is simply ignored.
>
> it's not ignored, it's used by the IR level optimizers. That way these
> optimizers can know stuff about the target without having to be linked
> to a target backend.

Well, it is used by one layer, ignored by another. Anyways LLVM is not
doing what the user expects.

>> LLVM uses the data layout
>> required by the native C ABI, which is hardcoded into LLVM's source
>> code. So I asked: pass the value setted by Module::setDataLayout to the
>> layers that are interested on it, as any user would expect.
>
> There are two classes of information in datalayout: things which correspond
> to stuff hard-wired into the target processor (for example that x86 is little
> endian), and stuff which is not hard-wired in (for example the alignment of
> x86 long double, which is 4 or 8 bytes on x86-32 depending on whether you are
> on linux, darwin or windows). Hoping to have code generators override the
> hard-wired stuff if they see something different in the data layout is just
> too much to ask for - eg the x86 code generators are never going to produce big
> endian code just because you set big-endianness in the datalayout. Even the
> second class of "soft" parameters is not completely flexible: for example most
> processors enforce a minimum alignment for types, and trying to reduce it by
> giving types a lesser alignment in the datalayout just isn't going to work.
> So given that the ways in which codegen could adapt to various datalayout
> settings are quite limited and constrained by the target, does it really make
> sense to try to parametrize the codegenerators by the datalayout at all?
> In any case, it might be good if the code generators produced a warning if they
> see that the datalayout string doesn't correspond to what codegen thinks it
> should be (I though someone added that already?).

You focus your reasoning on possible wrong uses of the data layout
setting (endianness) when, as you say, there are other uses which are
perfectly legit (using a specific alignment within the limits allowed by
the processor.) So if I need to align my data on a different way of
what the C ABI requires or generate code for a platform that LLVM still
does not know about, my only solution is to patch LLVM because the value
setted through one of its APIs is ignored on key places, as LLVM assumes
that everybody wants full interoperability with C. This is the kind of
logic that tells me that LLVM is a C-obsessed project: any requirement
that falls outside the needs of a C compiler writer is seen as
superfluous even if it does not conflict with the rest of LLVM.

Duncan Sands

unread,
Oct 5, 2011, 8:01:53 AM10/5/11
to llv...@cs.uiuc.edu
Hi Oscar,

>>> There are places where compatibility with the native C ABI is taken too
>>> far. For instance, time ago I noted that what the user sets through
>>> Module::setDataLayout is simply ignored.
>>
>> it's not ignored, it's used by the IR level optimizers. That way these
>> optimizers can know stuff about the target without having to be linked
>> to a target backend.
>
> Well, it is used by one layer, ignored by another. Anyways LLVM is not
> doing what the user expects.

it's not doing what *you* expect: it doesn't match your mental model of what
it is for (or should be for). The question is whether LLVM should be changed
or your expectations should be changed. Just observing the mismatch between
your expectations and current reality is not in itself an argument that LLVM
should be changed.

You are talking to the wrong person: I pretty much only use Ada not C, so I
don't think I'm C obsessed. Yet I never had any problems using LLVM with Ada.
LLVM gives you several mechanisms for aligning things the way you like. Are
they inadequate? Do you have a specific example of something you find
problematic?

Ciao, Duncan.

Duncan Sands

unread,
Oct 5, 2011, 8:04:20 AM10/5/11
to llv...@cs.uiuc.edu
>> So given that the ways in which codegen could adapt to various datalayout
>> settings are quite limited and constrained by the target, does it really make
>> sense to try to parametrize the codegenerators by the datalayout at all?

PS: This wasn't a rhetorical question, i.e. I wasn't saying that what you are
looking for is wrong. It was a real question about the design of LLVM.

Óscar Fuentes

unread,
Oct 5, 2011, 9:52:33 AM10/5/11
to llv...@cs.uiuc.edu
Hello Duncan.

Duncan Sands <bald...@free.fr> writes:

> it's not doing what *you* expect: it doesn't match your mental model of what
> it is for (or should be for). The question is whether LLVM should be changed
> or your expectations should be changed. Just observing the mismatch between
> your expectations and current reality is not in itself an argument that LLVM
> should be changed.

I see Module:setDataLayout, look at the documentation for the method,
see "Set the data layout" and think "gee, this is for setting the data
layout." I have no reason for thinking that the setting is only used on
parts of LLVM and ignored on others.

>> You focus your reasoning on possible wrong uses of the data layout
>> setting (endianness) when, as you say, there are other uses which are
>> perfectly legit (using a specific alignment within the limits allowed by
>> the processor.) So if I need to align my data on a different way of
>> what the C ABI requires or generate code for a platform that LLVM still
>> does not know about, my only solution is to patch LLVM because the value
>> setted through one of its APIs is ignored on key places, as LLVM assumes
>> that everybody wants full interoperability with C. This is the kind of
>> logic that tells me that LLVM is a C-obsessed project: any requirement
>> that falls outside the needs of a C compiler writer is seen as
>> superfluous even if it does not conflict with the rest of LLVM.
>
> You are talking to the wrong person: I pretty much only use Ada not C, so I
> don't think I'm C obsessed. Yet I never had any problems using LLVM
> with Ada.

I guess that your Ada compiler is required to be compatible with the
platform's C ABI, then.

> LLVM gives you several mechanisms for aligning things the way you
> like.

My problem is with the aligment of struct members. The programmer's
guide says that I can create packed structs (1-byte aligned) and

"In non-packed structs, padding between field types is inserted as
defined by the TargetData string in the module, which is required to
match what the underlying processor expects."

which is not true, unless "TargetData string" refers to what is
hard-coded into the LLVM backend, not to what is setted by
Module::setDataLayout. In any case, a clarification is highly needed
both on the method's documentation and on the programmer's guide.

It is true that I could pack all structs generated by my compiler and
insert padding as necessary, but this is really undesirable.

> Are
> they inadequate? Do you have a specific example of something you find
> problematic?

My compiler generates intructions for a virtual stack machine. It is
required to work wherever a good enough C++ compiler is available, with
no further intervention, so the arrangement of members in structures
assume a fixed data layout wich is conservative enough to not violate
any reasonable processor rules. The next key factor is that the language
allows execution of arbitrary code at compile-time (think of a Lisp-like
macro system) which includes the possibility of creating instances of
structs (not to confuse with C structs, which my implementation accesses
only through accessor functions.) Enter LLVM. As LLVM generates code
following the platform's C ABI, which is not necessarily the same as
what my compiler assumes, whenever code generated by LLVM accesses
structures instantiated by running code at compile-time (which was
executed by the virtual stack machine) nasty things happen.

David A. Greene

unread,
Oct 5, 2011, 11:12:19 AM10/5/11
to Dan Gohman, llvmdev@cs.uiuc.edu Mailing List
Dan Gohman <goh...@apple.com> writes:

> In this email, I argue that LLVM IR is a poor system for building a
> Platform, by which I mean any system where LLVM IR would be a
> format in which programs are stored or transmitted for subsequent
> use on multiple underlying architectures.

I agree with all of this. But...so what? :) It's a compiler IR.
That's not a bad thing.

Do you want to propose some changes to make LLVM IR more target
independent? I'm sure it would be welcome, but these are not easy
problems to solve, as you know. :)

-Dave

Dan Gohman

unread,
Oct 5, 2011, 11:20:34 AM10/5/11
to Renato Golin, llvmdev@cs.uiuc.edu Mailing List

On Oct 5, 2011, at 1:17 AM, Renato Golin wrote:

> On 5 October 2011 01:19, Chris Lattner <clat...@apple.com> wrote:
>> I'm not sure what you're getting at here. My email was not intended to say that I'm not interested in LLVM improving - quite the contrary. My email was to rebut Dan's implicit claim that PNaCL and using LLVM as a portable IR is never going to work. I'm arguing in the "opencl" and "pnacl" folks favor :)
>
> Hi Chris,
>
> Ok, I think I (now) get your drift. Let's restart the conversation, then. ;)
>
> If I got it right this time, from your point of view, Dan's arguments
> are not accurate because IR never intended to be anything else anyway.
> PNaCl, OpenCL, RenderScript, Java-like VMs were aggregated over time
> and tried to use IR for what it was not designed to be. I completely
> agree with you in that one.

Hi Renato,

I think you're overreacting here. There is nothing about OpenCL, RenderScript,
or VMKit that requires LLVM IR be used like a Platform, as I defined it in my
first paragraph. I'm aware that some people would like to use LLVM IR as a
Platform, and I'm saying that there are important high-level considerations
to make before doing so, and my impression is that there is little discussion of
issues I consider important.

Possibly it's too late for some though, and possibly people are getting too
caught up on the thorny ABI issues and missing my broader ideas.

Dan

Dan Gohman

unread,
Oct 5, 2011, 11:18:48 AM10/5/11
to Renato Golin, llvmdev@cs.uiuc.edu Mailing List

On Oct 5, 2011, at 1:17 AM, Renato Golin wrote:

> On 5 October 2011 01:19, Chris Lattner <clat...@apple.com> wrote:
>> I'm not sure what you're getting at here. My email was not intended to say that I'm not interested in LLVM improving - quite the contrary. My email was to rebut Dan's implicit claim that PNaCL and using LLVM as a portable IR is never going to work. I'm arguing in the "opencl" and "pnacl" folks favor :)
>
> Hi Chris,
>
> Ok, I think I (now) get your drift. Let's restart the conversation, then. ;)
>
> If I got it right this time, from your point of view, Dan's arguments
> are not accurate because IR never intended to be anything else anyway.
> PNaCl, OpenCL, RenderScript, Java-like VMs were aggregated over time
> and tried to use IR for what it was not designed to be. I completely
> agree with you in that one.

Hi Renato,

I think you're overreacting here. There is nothing about OpenCL, RenderScript,
or VMKit that requires LLVM IR be used like a Platform, as I defined it in my
first paragraph. I'm aware that some people would like to use LLVM IR as a
Platform, and I'm saying that there are important high-level considerations
to make before doing so, and my impression is that there is little discussion of
issues I consider important.

Possibly it's too late for some though, and possibly people are getting too
caught up on the thorny ABI issues and missing my broader ideas.

Dan

_______________________________________________

David A. Greene

unread,
Oct 5, 2011, 11:31:27 AM10/5/11
to Chris Lattner, llvmdev@cs.uiuc.edu Mailing List
Chris Lattner <clat...@apple.com> writes:

> That said, I'm trying to also inject realism. C is an inherently
> hostile language to try to get portability out of.

Aren't there (at least) two different aspects here? One is the source
language. C is not portable. There's nothing LLVM can do to fix that.
However, other HLLs are portable. What can LLVM do to help them?

The other aspect is the ABI. Right now LLVM pretty much assumes a
C-like ABI. That's only necessary for some C-derived languages.
Fortran, for example, has no defined ABI so the compiler is free to do
whatever it pleases. Fortran objects are not interchangeable among
compiler vendors and users are perfectly fine with that, though often us
compiler developers are not. :)

So to me this seems like a language issue and an ABI issue, neither one
of which need be tied directly to LLVM.

That said, I think creating a portable LLVM IR is a huge project (much
bigger than many people realize) and in the end, I don't think there's
much to be gained. A portable IR built on top of LLVM makes more sense
to me. Anyone interested in that should check read up on projects like
ANDF that produced volumes of papers on what's required.

-Dave

Kenneth Uildriks

unread,
Oct 5, 2011, 11:42:46 AM10/5/11
to Talin, llvmdev@cs.uiuc.edu Mailing List
_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

As a would-be language implementer, I see a few choices:

JVM: Severe restrictions on generated code, no structs or stack objects or pass-by-reference of stack values, heavy runtime that generated code must run inside of, strict type hierarchy, very little control over optimization.
CLR: Lags behind the JVM on non-Microsoft platforms.  Also a heavy runtime.  However, it has more flexibility in its generated code.
LLVM: Much, much more flexibility and optimization hooks than either JVM or CLR, but with piecemeal GC support, ABI complications, portability issues, JIT issues, and all the rest.

From what I can tell, there's a *huge* gap between LLVM on the one hand and JVM/CLR on the other... and having *something* in that gap would allow/encourage the development of a huge array of useful languages and runtimes that don't exist right now.

It seems far more plausible to me for LLVM to evolve into that "something" than for the JVM or CLR to do so.

Renato Golin

unread,
Oct 5, 2011, 11:58:38 AM10/5/11
to Dan Gohman, llvmdev@cs.uiuc.edu Mailing List
On 5 October 2011 16:18, Dan Gohman <goh...@apple.com> wrote:
> I think you're overreacting here.  There is nothing about OpenCL, RenderScript,
> or VMKit that requires LLVM IR be used like a Platform, as I defined it in my
> first paragraph.  I'm aware that some people would like to use LLVM IR as a
> Platform, and I'm saying that there are important high-level considerations
> to make before doing so, and my impression is that there is little discussion of
> issues I consider important.

Hi Dan,

I probably am. And that got in the way of highlighting the real issue.

As Kenneth just highlighted, there is a gap, and people are trying to
fill that gap. I personally prefer to use LLVM for that job, instead
of Java bytecode, and it seems many people feel the same.

So, let's separate the issues here:

1) LLVM IR is a compiler IR, nothing else, nor should be. I agree
with David, this is not a bad thing. Case closed.

2) Has LLVM the potential to fill that gap via other routes?

Can LLVM do (one day) what you wanted it to do today? A higher level
representation for light-weight JITs, a rich type-system for complex
linkage of higher languages, special semantics for special purposes,
etc.

Today, LLVM IR is NOT portable, so why worry about the portability of
DSLs around it? OpenCL rep. can be different (in syntax, and
semantics) from C++ rep., but it should be similar to RenderScript
rep. If you want to link CL and C++ reps, lower them to LLVM IR, or
use the common subset.


--
cheers,
--renato

http://systemcall.org/

_______________________________________________

Justin Holewinski

unread,
Oct 5, 2011, 1:03:56 PM10/5/11
to Renato Golin, llvmdev@cs.uiuc.edu Mailing List
It seems to me like that real issue is: should LLVM IR be platform/architecture-agnostic? It seems pretty clear that it currently is not, and as others have pointed out, I do not see this being a particular problem.

I see people comparing LLVM IR to Java/CLR bytecode, but I'm not sure that is the right comparison to be making.  The way I see LLVM, it's the lower-level, platform-specific equivalent of platform-independent Java/CLR bytecode.  Some optimizations/transformations/analyses are better performed on high-level representations, and some on the low-level representations.  So why must LLVM try to meet *both* goals?  Instead, different types of front-ends can use custom intermediate representations that meet their needs, and then lower to platform-specific LLVM IR before final code emission.  I'm afraid that if LLVM gets into the game of trying to be the intermediate representation for *everything*, then it will suffer.
 


--
cheers,
--renato

http://systemcall.org/

_______________________________________________
LLVM Developers mailing list
LLV...@cs.uiuc.edu         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev



--

Thanks,

Justin Holewinski

Dan Gohman

unread,
Oct 5, 2011, 1:09:36 PM10/5/11
to Talin, llvmdev@cs.uiuc.edu Mailing List
On Oct 4, 2011, at 10:29 PM, Talin wrote:
>
> Here's my position in a nutshell: The kind of things that Dan wants LLVM to do should really be a separate sub-project from LLVM proper, built on top of LLVM. I think it's unrealistic to expect LLVM proper to adopt Dan's stated objectives - but at the same time, it would be a awful shame if there wasn't something that could meet his needs, since I think many people other than Dan would benefit from such a thing.

There seems to be substantial confusion about this, here and elsewhere
on the internet. I personally am not proposing a new IR, or a new
project, or any new development effort here. I'm just making observations,
some of which are widely known, some of which are not, and proposing a
conclusion, for the purpose of promoting understanding.

In the paragraph where I discussed the task of an independent
implementaion, I meant it as a purely hypothetical situation.

Dan

Christophe de Dinechin

unread,
Oct 5, 2011, 4:55:17 AM10/5/11
to Dan Gohman, llvmdev@cs.uiuc.edu Mailing List
Hi Dan,


I read five distinct requests in your well-written remarks, which may appeal to different people:

1. How can we make LLVM more portable? As Chris later pointed out, it's hard to achieve this goal on the input side while preserving C semantics, since even C source code doesn't really have that property. On the platform front, recent discussions about "non-standard" architectures highlighted that most of the LLVM effort is really around x86 and ARM, and platforms that deviate from these reference points tend to be second thoughts.

2. How can we make LLVM more stable over time? As a regular user of LLVM, I initially found the frequent changes in LLVM painful. On the other hand, that effort is not a high price if it keeps the code base fluid. It wouldn't hurt to take an approach like OpenGL where new stuff is tested through a shared "extensions" mechanism, and deprecation of old interfaces spans years. "It no longer works" is a message we see a little too often on LLVM-dev.

3. How can we clarify the specification of LLVM? In the good old Unix tradition, the source code is the documentation, and the "documentation" explains the bugs and gives simplistic examples. But standard-level specification is really hard and tends to spend inordinate amount of time on corner cases ordinary folks don't care about. To wit: C++ and C++ ABI standardization efforts. LLVM has the luxury to be able to just assert in the corner cases, and deal with it on demand.

4. How can we address minority needs in LLVM? Being a minority here, I can only second that. I'd say that LLVM has to keep their priorities right. As someone else pointed out, one reason to pick up LLVM is because it gives me interoperability with C. I'm not willing to give that up, and that means I have to learn a little bit of the C non-portable way of doing things. That being said, minorities are also the guys keeping you on your toes.

5. How can we avoid selfish kludges and self-imposed limitations in the LLVM source code base? Probably the more immediately actionable point. IMO, things tend to go in the right direction, at least in my experience. But it's always easy to lapse.

Overall, I see these not so really as technical or architectural issues. Rather, I'd say that LLVM is very "market driven", i.e. the largest communities (C and x86) tend to grab all the attention. Still, it has reached a level of maturity where even smaller teams like ours can benefit from the crumbs.


That being said, can we build a portable LLVM IR on top of the existing stuff without giving up C compatibility? I'm not sure. I would settle for a few sub-goals that may be more easily achieved, e.g. define a subset of the IR that is exactly as portable as C, or ensuring that object layout settings default to the target, but can effectively be overridden in a meaningful way (think: C++ ABI inheritance rules, HP28/HP48 internal object layout, ...)


My two bytes
Christophe

On 4 oct. 2011, at 20:53, Dan Gohman wrote:

> In this email, I argue that LLVM IR is a poor system for building a
> Platform, by which I mean any system where LLVM IR would be a
> format in which programs are stored or transmitted for subsequent
> use on multiple underlying architectures.
>

> LLVM IR initially seems like it would work well here. I myself was
> once attracted to this idea. I was even motivated to put a bunch of
> my own personal time into making some of LLVM's optimization passes
> more robust in the absence of TargetData a while ago, even with no
> specific project in mind. There are several things still missing,
> but one could easily imagine that this is just a matter of people
> writing some more code.
>
> However, there are several ways in which LLVM IR differs from actual
> platforms, both high-level VMs like Java or .NET and actual low-level
> ISAs like x86 or ARM.
>
> First, the boundaries of what capabilities LLVM provides are nebulous.
> LLVM IR contains:
>
> * Explicitly Target-specific features. These aren't secret;
> x86_fp80's reason for being is pretty clear.


>
> * Target-specific ABI code. In order to interoperate with native
> C ABIs, LLVM requires front-ends to emit target-specific IR.
> Pretty much everyone around here has run into this.
>

> * Implicitly Target-specific features. The most obvious examples of
> these are all the different Linkage kinds. These are all basically
> just gateways to features in real linkers, and real linkers vary
> quite a lot. LLVM has its own IR-level Linker, but it doesn't
> do all the stuff that native linkers do.
>
> * Target-specific limitations in seemingly portable features.
> How big can the alignment be on an alloca? Or a GlobalVariable?
> What's the widest supported integer type? LLVM's various backends
> all have different answers to questions like these.
>
> Even ignoring the fact that the quality of the backends in the
> LLVM source tree varies widely, the question of "What can LLVM IR do?"
> has numerous backend-specific facets. This can be problematic for
> producers as well as consumers.
>
> Second, and more fundamentally, LLVM IR is a fundamentally
> vague language. It has:
>
> * Undefined Behavior. LLVM is, at its heart, a C compiler, and
> Undefined Behavior is one of its cornerstones.
>
> High-level VMs typically raise predictable exceptions when they
> encounter program errors. Physical machines typically document
> their behavior very extensively. LLVM is fundamentally different
> from both: it presents a bunch of rules to follow and then offers
> no description of what happens if you break them.
>
> LLVM's optimizers are built on the assumption that the rules
> are never broken, so when rules do get broken, the code just
> goes off the rails and runs into whatever happens to be in
> the way. Sometimes it crashes loudly. Sometimes it silently
> corrupts data and keeps running.
>
> There are some tools that can help locate violations of the
> rules. Valgrind is a very useful tool. But they can't find
> everything. There are even some kinds of undefined behavior that
> I've never heard anyone even propose a method of detection for.
>
> * Intentional vagueness. There is a strong preference for defining
> LLVM IR semantics intuitively rather than formally. This is quite
> practical; formalizing a language is a lot of work, it reduces
> future flexibility, and it tends to draw attention to troublesome
> edge cases which could otherwise be largely ignored.
>
> I've done work to try to formalize parts of LLVM IR, and the
> results have been largely fruitless. I got bogged down in
> edge cases that no one is interested in fixing.
>
> * Floating-point arithmetic is not always consistent. Some backends
> don't fully implement IEEE-754 arithmetic rules even without
> -ffast-math and friends, to get better performance.
>
> If you're familiar with "write once, debug everywhere" in Java,
> consider the situation in LLVM IR, which is fundamentally opposed
> to even trying to provide that level of consistency. And if you allow
> the optimizer to do subtarget-specific optimizations, you increase
> the chances that some bit of undefined behavior or vagueness will be
> exposed.
>
> Third, LLVM is a low level system that doesn't represent high-level
> abstractions natively. It forces them to be chopped up into lots of
> small low-level instructions.
>
> * It makes LLVM's Interpreter really slow. The amount of work
> performed by each instruction is relatively small, so the interpreter
> has to execute a relatively large number of instructions to do simple
> tasks, such as virtual method calls. Languages built for interpretation
> do more with fewer instructions, and have lower per-instruction
> overhead.
>
> * Similarly, it makes really-fast JITing hard. LLVM is fast compared
> to some other static C compilers, but it's not fast compared to
> real JIT compilers. Compiling one LLVM IR level instruction at a
> time can be relatively simple, ignoring the weird stuff, but this
> approach generates comically bad code. Fixing this requires
> recognizing patterns in groups of instructions, and then emitting
> code for the patterns. This works, but it's more involved.
>
> * Lowering high-level language features into low-level code locks
> in implementation details. This is less severe in native code,
> because a compiled blob is limited to a single hardware platform
> as well. But a platform which advertizes architecture independence
> which still has all the ABI lock-in of HLL implementation details
> presents a much more frightening backwards compatibility specter.
>
> * Apple has some LLVM IR transformations for Objective-C, however
> the transformations have to reverse-engineer the high-level semantics
> out of the lowered code, which is awkward. Further, they're
> reasoning about high-level semantics in a way that isn't guaranteed
> to be safe by LLVM IR rules alone. It works for the kinds of code
> clang generates for Objective C, but it wouldn't necessarily be
> correct if run on code produced by other front-ends. LLVM IR
> isn't capable of representing the necessary semantics for this
> unless we start embedding Objective C into it.
>
>
> In conclusion, consider the task of writing an independent implementation
> of an LLVM IR Platform. The set of capabilities it provides depends on who
> you talk to. Semantic details are left to chance. There are features
> which require a bunch of complicated infrastructure to implement which
> are rarely used. And if you want light-weight execution, you'll
> probably need to translate it into something else better suited for it
> first. This all doesn't sound very appealing.


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

Renato Golin

unread,
Oct 5, 2011, 4:47:23 PM10/5/11
to Justin Holewinski, llvmdev@cs.uiuc.edu Mailing List
On 5 October 2011 18:03, Justin Holewinski <justin.h...@gmail.com> wrote:
>  So why must LLVM try to meet *both* goals?  Instead, different types of
> front-ends can use custom intermediate representations that meet their
> needs, and then lower to platform-specific LLVM IR before final code
> emission.  I'm afraid that if LLVM gets into the game of trying to be the
> intermediate representation for *everything*, then it will suffer.

Hi Justin,

You seem to be intermixing LLVM vs. LLVM IR.

I think LLVM can have as many sub-projects as people want to, and they
can create as many new shiny things as they want. LLVM IR, on the
other hand, has specific goals and should keep tight to it.

As I said before, IR is what it is. But LLVM is not *just* the IR...
There is a lot more that can be done, and Polly and OpenCL are just
the beginning...

Justin Holewinski

unread,
Oct 5, 2011, 5:04:32 PM10/5/11
to Renato Golin, llvmdev@cs.uiuc.edu Mailing List
On Wed, Oct 5, 2011 at 4:47 PM, Renato Golin <reng...@systemcall.org> wrote:
On 5 October 2011 18:03, Justin Holewinski <justin.h...@gmail.com> wrote:
>  So why must LLVM try to meet *both* goals?  Instead, different types of
> front-ends can use custom intermediate representations that meet their
> needs, and then lower to platform-specific LLVM IR before final code
> emission.  I'm afraid that if LLVM gets into the game of trying to be the
> intermediate representation for *everything*, then it will suffer.

Hi Justin,

You seem to be intermixing LLVM vs. LLVM IR.

Right, sorry, I meant LLVM IR.  I'm not clear to me that there is any significant advantage to making LLVM *IR* platform/architecture-agnostic.  The benefits may not outweigh the disadvantages.
 

I think LLVM can have as many sub-projects as people want to, and they
can create as many new shiny things as they want. LLVM IR, on the
other hand, has specific goals and should keep tight to it.

Yes, I agree 100%.  I would much rather see LLVM IR stay platform-dependent, and let different higher-level representations be used for platform-agnostic work.
 

As I said before, IR is what it is. But LLVM is not *just* the IR...
There is a lot more that can be done, and Polly and OpenCL are just
the beginning...



--
cheers,
--renato

http://systemcall.org/



--

Thanks,

Justin Holewinski

Joachim Durchholz

unread,
Oct 5, 2011, 5:16:47 PM10/5/11
to llv...@cs.uiuc.edu
Now that the dust begins to settle... I'm wondering whether LLVM is for me.

I'm working on something that can be used to create software for
different environments: C/C++, JVM, CLR, Parrot, etc.
I.e. one language for different environments, but not write once, run
anywhere.

Now what would be the role of LLVM in such an infrastructure?
Just backend for C/C++ linkage, and I should go and look elsewhere for
JVM/CLR/whateverVM?
Should I look into LLVM subprojects? Which ones?

Regards,
Jo

David A. Greene

unread,
Oct 5, 2011, 6:10:36 PM10/5/11
to Joachim Durchholz, llv...@cs.uiuc.edu
Joachim Durchholz <j...@durchholz.org> writes:

> Now that the dust begins to settle... I'm wondering whether LLVM is for me.
>
> I'm working on something that can be used to create software for
> different environments: C/C++, JVM, CLR, Parrot, etc.
> I.e. one language for different environments, but not write once, run
> anywhere.
>
> Now what would be the role of LLVM in such an infrastructure?
> Just backend for C/C++ linkage, and I should go and look elsewhere for
> JVM/CLR/whateverVM?
> Should I look into LLVM subprojects? Which ones?

It depends on what you want to do with the IR. If you want to create
object files, LLVM is great. You just need to map the semantics of the
various HLLs onto the LLVM IR language, as with any translator. For any
kind of code-generator-ish thing, it's hard to beat LLVM IR, IMHO.

If you want to JIT, then some of LLVM IR's limitations will impact the
speed of code generation, as Dan outlined.

If you want to do fancy transformations that use or analyze high-level
language semantics, LLVM IR may not be right for you, as most of that
information is lost by the time the code has been converted to LLVM IR.

-Dave

Michael Clagett

unread,
Oct 6, 2011, 2:29:26 PM10/6/11
to llv...@cs.uiuc.edu
Hi Folks --

Let me go ahead and pose a question similar to the one Joachim poses below.  I too am trying to evaluate whether LLVM will be of use to me in building a compiler and garbage collection mechanism for a byte code vm that I have built.  Although there are multiple types of code that can be created with this system, all of them eventually translate to execution of one or more of these byte codes -- with the exception of stuff that's coded directly in Intel assembly language (which I understand can be output as is and left untouched by the LLVM compiler if desired).  There's about 32 core op codes that constitute the basic instruction set and I can envision mapping each of these to some sequence of LLVM IR.   There's also a whole lot more "extended opcodes" that are executed by the same core instruction execution loop but which are coded using the built-in Intel assembler and added dynamically by the system.  I could envision also going to the trouble of mapping each of these to a sequence of LLVM IR instructions and then being able to emit a series of LLVM IR sequences purely based on the sequence of vm opcodes encountered in a scan of code compiled for the vm. 

I'm hoping that such a product could then be submitted to all the LLVM optimizations and result in better Intel assembly code generation than what I have hand-coded myself (in my implementations of either the core or the extended opcodes -- and especially in the intel code sequences resulting from the use of these opcodes in sequences together).  So first question is simply to ask for a validation of this thinking and whether such a strategy seems feasible.

The second question pertains to this discussion thread.  If at the end of the day, all I am trying to do is compile for an 80x86 platform (although ideally hoping to target Windows, Linux and the Mac) and don't need to target multiple processors, then LLVM should add significant value for me if the answer to the first question is that it is a sound and sensible strategy.  And most of the discussion in this thread about platform-specific issues shouldn't apply if I only have one processor type to target.  Am I thinking about this correctly?

Any insights from some of you old hands would be greatly appreciated.  

Thanks.

Mike


Message: 1
Date: Wed, 05 Oct 2011 17:10:36 -0500
From: gre...@obbligato.org (David A. Greene)
Subject: Re: [LLVMdev] LLVM IR is a compiler IR
To: Joachim Durchholz <j...@durchholz.org>
Cc: llv...@cs.uiuc.edu
Message-ID: <nngk48j...@transit.us.cray.com>
Content-Type: text/plain; charset=us-ascii

David A. Greene

unread,
Oct 6, 2011, 3:02:48 PM10/6/11
to Michael Clagett, llv...@cs.uiuc.edu
Michael Clagett <mcla...@hotmail.com> writes:

> There's about 32 core op codes that constitute the basic instruction
> set and I can envision mapping each of these to some sequence of LLVM
> IR. There's also a whole lot more "extended opcodes" that are
> executed by the same core instruction execution loop but which are
> coded using the built-in Intel assembler and added dynamically by the
> system. I could envision also going to the trouble of mapping each of
> these to a sequence of LLVM IR instructions and then being able to
> emit a series of LLVM IR sequences purely based on the sequence of vm
> opcodes encountered in a scan of code compiled for the vm.

> I'm hoping that such a product could then be submitted to all the LLVM
> optimizations and result in better Intel assembly code generation than
> what I have hand-coded myself (in my implementations of either the
> core or the extended opcodes -- and especially in the intel code
> sequences resulting from the use of these opcodes in sequences
> together). So first question is simply to ask for a validation of
> this thinking and whether such a strategy seems feasible.

Let me make sure I'm understanding you correctly. You want to map each
of you opcodes into an LLVM sequence and then use the LLVM optimizations
and JIT to generate efficient native code implementations? Then you
would invoke those implementations during interpretation?

Or is it that you want to take a bytecode program, map it to LLVM IR,
run it through optimizations and codegen to produce a native executable?

Either one of these will work and LLVM seems like a good match as long
as you don't expect the optimizations to understand the higher-level
semantics of your opcodes (without some work by you, at least).

I don't quiet grasp any benefit to the first use as I would just go
ahead and generate the optimal native code sequence for each opcode once
and be done with it. No LLVM needed at all. So I suspect this is not


what you want to do

-Dave

Michael Clagett

unread,
Oct 6, 2011, 4:07:04 PM10/6/11
to llv...@cs.uiuc.edu
Sorry, menat to upload this to the list.

> From: gre...@obbligato.org
> To: mcla...@hotmail.com
> CC: llv...@cs.uiuc.edu
> Subject: Re: [LLVMdev] LLVM IR is a compiler IR

Michael Clagett

unread,
Oct 6, 2011, 4:24:03 PM10/6/11
to llv...@cs.uiuc.edu
Sorry for the noise, but this is the message I meant to send to the list rather than replying to David directly.  Unfortunately, I just sent his message to me before.


From: mcla...@hotmail.com
To: gre...@obbligato.org
Subject: RE: [LLVMdev] LLVM IR is a compiler IR
Date: Thu, 6 Oct 2011 19:44:11 +0000

Thanks for your prompt reply.  My answers are below at the end of your message.

> Subject: Re: [LLVMdev] LLVM IR is a compiler IR
> Date: Thu, 6 Oct 2011 14:02:48 -0500
>
It is actually the first of your alternatives above that I was hoping to achieve and the reason I was thinking this would be valuable is twofold.   First, I don't have so much faith in the quality or optimal character of my own byte code implementations.   The core 32 opcodes tend to be high-level implementations of low-level operations in dealing with the elements of the virtual machine -- things like stack operations on the two built in stacks of the virtual machine or movements to and from the vm's address register or access to and from the addresses moved there.  Other core opcodes include '2*' (multiply top of stack by 2) 'COM' (one's complement of top of stack)  ';' (jump vm instruction ptr to address on top of return stack) and that sort of thing.  The top of data and return stacks are mapped to register EAX and EDI, respectively, and the address reg is mapped to ESI.  But any stack operations that involve' push'-ing, 'pop'-ing, 'dup'-ing, 'drop'-ing, etc. end up going to memory storage where the bulk of the stack storage lives.  Each of these core primitives are on average around 10 intel instructions long, and many of the extended opcodes that have been coded are there to bypass more costly sequences of core primitives that they replace.  It was my general feeling that a good SSA-based compilation mechanism like that of LLVM could do a better job at maximizing the use of the Intel's limited resources than I could.

Moreover, as long as code remains at the VM instruction level, these resources are even more constrained than usual.  EDX needs to be preserved to hold the VM instruction pointer.  EAX, ESI and EDI need to be preserved for the purposes outlined above.  So there is an advantage in compiling VM opcode sequences to assembler that can violate these invariants on a more extended basis and use the entire register set for a longer period of time.  Similar considerations apply to simply reducing from 10 instructions to 1 or 2 instructions operations that at the VM level require the stack, but that at the intel assembler level would more naturally be handled in registers.

Finally, I just have the general feeling that more significant compiler optimizations can be effected across sequences of what are my vm opcode implementations.  This is a general feeling, but I'm hoping fairly well-founded.

Hope that explains my thinking better.  Does that change at all your view of the benefits that  I might achieve from LLVM?

Thanks.

Mike

David A. Greene

unread,
Oct 6, 2011, 5:20:53 PM10/6/11
to Michael Clagett, llv...@cs.uiuc.edu
Michael Clagett <mcla...@hotmail.com> writes:

> It is actually the first of your alternatives above that I was hoping
> to achieve and the reason I was thinking this would be valuable is
> twofold. First, I don't have so much faith in the quality or optimal
> character of my own byte code implementations. The core 32 opcodes
> tend to be high-level implementations of low-level operations in
> dealing with the elements of the virtual machine

Ok, so these are mildly complex operations. It makes sense to start
with some kind of machine-generated asm implementation to get optimized
performance. Don't write the opcode implementations in asm directly.
Write them in a high level language and compile them to native code.
See below.

> The top of data and return stacks are mapped to register EAX and EDI,
> respectively, and the address reg is mapped to ESI.

Does this have to be the case? See below.

> It was my general feeling that a good SSA-based compilation mechanism
> like that of LLVM could do a better job at maximizing the use of the
> Intel's limited resources than I could.

As an alternative to using the JIT, would it be possible to implement
each opcode in its own interpreter function and compile them statically?
Of course there would be call overhead interpreting each opcode. Once
you've got that you could apply various techniques such as threading the
interpreter (not multiprocessing, but threading the interpreter as in
http://en.wikipedia.org/wiki/Threaded_code) to eliminate the overhead.

I don't think there's any particular reason to rely on the JIT unless
you want to take it a bit further and optimize a specific sequence of
opcodes seen when interpreting a specific program. But then we're
getting into the various Futamura transformations. :)

> Moreover, as long as code remains at the VM instruction level, these
> resources are even more constrained than usual. EDX needs to be
> preserved to hold the VM instruction pointer. EAX, ESI and EDI need
> to be preserved for the purposes outlined above.

Why do those registers need to be preserved? Imagine the interpreter
were written completely in a high level language. The compiler doesn't
care which register holds a stack pointer, data pointer, etc. as long as
the virtual machine's state is consistent.

> Similar considerations apply to simply reducing from 10 instructions
> to 1 or 2 instructions operations that at the VM level require the
> stack, but that at the intel assembler level would more naturally be
> handled in registers.

Ah, ok, this is interesting. You want to change the execution model
on-the-fly. A grad school colleague of mine did something very similar
to this, translating a stack machine into a register machine. Of course
he's a hardware nut so he designed hardware to do it. :) Unfortunately,
I don't think he ever published anything on it.

Doing the threading thing mentioned above or the JIT/Dynamo thing
mentioned below can both accomplish this, I think, and without any
register constraints if I'm understanding you correctly.

> Finally, I just have the general feeling that more significant
> compiler optimizations can be effected across sequences of what are my
> vm opcode implementations. This is a general feeling, but I'm hoping
> fairly well-founded.

Yes, that's true. See the Futamura reference above. Given a VM and an
input program, you can in fact generate an optimized executable. This
is the logical extension of what you're getting at.

For this kind of thing a JIT makes sense. You might have a look at what
the HP people did with Dynamo. They got a lot of performance out of
translating PA-RISC to PA-RISC by doing exactly what you describe.

> Hope that explains my thinking better. Does that change at all your
> view of the benefits that I might achieve from LLVM?

It doesn't change it in the sense that I think LLVM will work well for
this. JIT speed could be an issue but that will be amortized if the
opcode sequence is executed enough times.

One way to speed up the JIT is to pre-generate a set of instruction
templates for each opcode that get filled in with specific information
available at runtime. See the papers on DyC for some examples. I
believe the Dynamo folks also took this route. This would be quite
extensive work in LLVM but would be very valuable, I think. Partial
evaluation papers may also be useful to explore.

HTH.

azakai

unread,
Oct 7, 2011, 2:49:58 PM10/7/11
to llv...@cs.uiuc.edu

I thought I'd offer the perspective from another project
that uses LLVM and needs to be cross platform, Emscripten.
Emscripten takes LLVM assembly and compiles that into JavaScript.
We have had to implement a lot of hacks to work around
portability issues in LLVM assembly, but overall *un*-optimized
assembly is not that hard to get working. Optimized assembly
is a different story, but since the target language is JS
that isn't too much of a concern (we can do standard compiler
optimizations after translation to JS, using Closure Compiler
and other tools, with good results - since it is then done
at the level of JS).

Overall LLVM has been extremely useful for Emscripten
because it is very well documented and developed and has
a friendly community.

If LLVM assembly could be portable (no hardcoded structure
indexes, no hardcoded sizes, etc. etc.) then that would be
much better for Emscripten obviously. But I think that
even without that, LLVM assembly is already very usable
for some types of projects that require portability. So,
given how difficult portability is to get right, and that it
typically requires compromises elsewhere, my recommendation
would be to consider such things very carefully. I'd love
for there to be some way to generate portable assembly, but
I am not sure LLVM should try to do that - it does other
things, and it does them extremely well.

- azakai

Reply all
Reply to author
Forward
0 new messages