Hacker News new | past | comments | ask | show | jobs | submit login

With Bitcode, Apple could change OS X into something like the old TAOS jit based OS. Except for a small kernel, all TAOS executables were intermediate representation files. This IR could be translated to real machine code at the same speed as disk access, and resulted in code running at 80-90% native speed on most platforms.

With software like that, Apple could become independent of any particular software architecture.

(TAOS dates from the 90's and is hard to google, but is mentioned in some papers. And yes, the JIT translator could do that even on 90's machines.)




"With software like that, Apple could become independent of any particular software architecture. "

Not with the languages they use :)

Neither C, nor C++, nor swift, can be made portable to new architectures through bitcode.

At least, not without language-level changes to each of them.

For example, for C and C++, sizeof is a constant expression, so you can't easily just do something like "defer evaluation to runtime". Plus ifdefs, struct layout, etc.

ANDF tried to solve these problems many years back. It may have even been "mostly possible" with c89. But today's languages, not so much.

(Even things like PNaCL and emscripten and what have you have restrictions on what C++ they allow)


> For example, for C and C++, sizeof is a constant expression, so you can't easily just do something like "defer evaluation to runtime". Plus ifdefs, struct layout, etc.

What prevents things like sizeof(T) or alignof(T) from being representable in the LLVM IR in a form that says, "defer to final translation" (to the target architecture)? Does substitution of platform-dependent types, for example, i64 for size_t on x86_64, happen prior to generating the LLVM IR? It would seem useful for me for LLVM IR to retain some platform-dependent types like size_t or intptr_t (deferring until final translation from bitcode to machine code), but maybe that would inhibit certain optimizations.


The short version is, that just isn't possible. Too many things would be unknown, and it is easy in both C and C++ to, at compile time, check the sizeof(size_t) and perform totally different behaviour depending on the value.


> The short version is, that just isn't possible. Too many things would be unknown, and it is easy in both C and C++ to, at compile time, check the sizeof(size_t) and perform totally different behaviour depending on the value.

I can see an issue with template instantiation in C++: for example, if a template uses SFINAE to specialize a template for types of certain sizes, that needs to be evaluated purely at the C++->LLVM stage of compilation.

What is the compile time part of C do you mean? sizeof(T) is evaluated at compile time of course, but it would still produce pseudocode like:

    if (4 < 16) { // sizeof(T) replaced with 4
        // do something
    } else {
        // do something else
    }
Of course, an optimizer would likely constant-fold that conditional expression to remove the branch entirely, but I'm having a difficult time seeing how one could perform different behavior at compile time with sizeof(T) in C.


#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

https://scaryreasoner.wordpress.com/2009/02/28/checking-size...

There are many ways to make it perform different behavior at compile time (though admittedly, most are abuse). The above should compile error, but if you push sizeof evaluation, will not.


Better form, for certain definitions of better (works in global scope):

    #define BUILD_BUG_ON(condition) extern int build_bug_on[!!(condition)-1]


It's worse than this. For example, you'd never be able to issue diagnostics about constexpr's. You'd also not be able to layout structs, etc.


"Does substitution of platform-dependent types, for example, i64 for size_t on x86_64, happen prior to generating the LLVM IR? "

In LLVM world, Clang is performing most of the ABI lowering.

For C and C++, the answer is "yes", and "it must happen this way", because struct layout/etc will depend on it. Not to mention what you want is at some level, impossible in the LLVM IR. LLVM types are not C/C++ types. This leaves you with no type system capable to do the kind of thing you want to do :)


Good point.

One minor caveat: ~16 years ago, in C99, C got variable-length arrays (VLAs). If you use the sizeof operator on a VLA, it is evaluated at runtime.

Of course that doesn't change your argument; I just wanted to mention it for completeness' sake.


Sounds somewhat similar to the IBM AS/400 (renamed many times). Applications were shipped as byte code and translated to the local machine architecture. The translated byte code was appended to the application, a but like NeXT fat binaries or OS-X's universal binaries.

The native byte code was 64bit, though iirc the first implementation used a 32bit address. The result was you could ship an application once and when it came time to move architectures all you had to do is one command to retranslate the byte code.

Very neat. I'm not sure why this approach hasn't been used more often.

EDIT: Looks like they're going exactly the AS/400 route.


Actually AS/400 byte code has 128 bit pointers. It was originally implemented on a 48 bit processor (weird 70's architectures FTW!).


I stand corrected. Thanks.

AS/400 had some really cool technology built into it.


The book "Inside the AS/400" has some really cool exposition on the unique aspects of the AS/400. If I remember right, the disks and memory are in a single address space, and of course, there is a relational database built in to the operating system.

I have seen the binary translation in action: it was required when POWER6 machines came out. On a POWER5 machine, I remember it taking around 20 minutes for an application that was around 20 MB zipped, but for software licensing reasons not all of the server models exposed the full performance of the processor, so that might not be representative. And at any rate, like the OP said, it's a one time thing, and then the translated version is kept.


There was the OpenGroup's Architecture Neutral Distribution Format, but I'm not sure if it was ever fully realized. Around the time that was proposed some real serious energy was put in to C compiler optimization, across the industry.

It does free them up in the future, they can make bigger hardware changes and just have to supply a bitstream player for the older code. I have a difficult time envisioning the architecture changes that make tons of sense to Apple right now though, other than like swapping video on their ARM chips and stuff like that. Running iOS apps on the desktop might make a ton of sense for some of them or some sort of tweener between the iPad and Mac book.


We were thinking on the same lines. IBM System/38 was truly a brilliant, integrated design in so many ways. Even had capability-security built into the hardware. Check it out at the reference below. Intel made a monster of a system too. Would love to have either on a SOC or an inexpensive server.

http://homes.cs.washington.edu/~levy/capabook/index.html


You mean just like Java? The performance overhead of Java versus native code is pretty negligible at this point, and Swift is pretty similar to Java 8. I suspect if Apple wanted to go the intermediate compilation route, they would have just used Java.

I suspect Bitcode is more about helping developers ship a single binary that works across 7 or 8 devices with slightly different ISAs. App thinning is a big push for Apple right now because app sizes are getting out of control with the number of variants that developers are required to compile to. Developers compile to Bitcode, then upload to Apple, who then compiles multiple versions of the machine code, using the App Store to download the correct bundle for the device.


Every single binary every emitted by LLVM has been Bitcode during the compilation process. The only new element here is that Apple's asking for this intermediate product in order to be on the App Store.


Yep, and what I'm saying is that Bitcode doesn't help portability; only in producing device-optimized binaries for variants of the same ISA. The end goal here is reducing app size, not making it easier to port things to a new ISA. A new ISA wouldn't be impossible -- Apple is one of the few companies to successfully navigate this transition -- but IMO Apple would be better served on the Mac side by lower-power x86 parts (which are finally available) than higher-performing ARM CPUs.


In my industry, app data size swamps app executable size by a factor of about 2000.. So all this app thinning stuff seems useless (to me).


That's not universally true (as you seem to indicate already). For example, I included Google's WebRTC libraries in an iOS app and it bloated the binary by several hundred megabytes per architecture. Thinning that would help a lot.

Resources are a big part of it too, of course. A typical iOS app these days has three copies of every image, for 1x, 2x, and 3x resolutions. Dropping that down to one copy on the device is helpful. Of course this doesn't require any of this bitcode stuff, although neither does thinning the executable.


FFmpeg on Android also tends to make apps ridiculously huge when needed due to the need to support multiple architectures. Worse if you want something like x264 as well.


Well-optimized, lean apps often use cache and registers more efficiently. This brings performance boosts. That can save real $$$ on hardware or just let you do more with it.


TAOS, what a blast from the past. That system was cool. Sad that it never came to be.




Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: