roboscala / sbt-robovm

An sbt plugin for iOS development in Scala
BSD 2-Clause "Simplified" License
108 stars 16 forks source link

Adding almost any code to the hello demo breaks the robovm compilation for iOS. #26

Closed windemut closed 9 years ago

windemut commented 9 years ago

I have posted this over at the scala-ios-demos in more detail (https://github.com/roboscala/scala-ios-demos/issues/1). Not sure anyone's listening there, so mention it here. It seems strange that you all appear to be not affected by it.

I am trying to use sbt-robovm to run the scala demo apps on the IOS simulator.

I have gotten the "empty" and "hello" demos running just fine, using the iphone-sim target. However, when I try to add more code, I get messages such as

Warning: scala.math.BigDecimal$$anonfun$equals$1 is a phantom class! (A varying number of different classes, depending on what code is added)

followed by

LLVM ERROR: ran out of registers during register allocation

Strenuous googling has convinced me that there is a stubborn issue in LLVM 3.4 that causes the latter error in roboVM for any code that references Predef. The "empty" and "hello" demos appear to be simple enough that they don't, but adding almost any Scala code to them triggers the error. Notably, a string concatenation such as ("Click #"+clickCount) seems to set it off, among many other things. The best location to track this error I found is here: https://github.com/robovm/robovm/issues/313.

Perhaps the easiest question I have is why has no-one working with sbt-robovm observed this issue, apparently? Is no-one using the iphone-sim target or is no-one compiling anything other than the demos? Or, most likely, is there something I am overlooking? Absent a simple explanation, perhaps someone could give it a quick try by replacing "Hello, Robo" with "Hello Robo "+1, to see if the error reproduces in hands other than mine.

It is a shame, it would be really nice to be able to do IOS using Scala. As it is, there appears to be no way, at least until they fix that LLVM bug.

Thanks!

windemut commented 9 years ago

On a related note: Thinking I'd try fixing it myself, using a patch the LLVM guys have, I ran into a dead end when I noticed that RoboVM apparently includes binary libraries in the source tree for which there is no source. Specifically, "llvm/src/main/resources/org/robovm/llvm/binding/macosx/x86_64/librobovm-llvm.dylib" and 2 other libraries are checked into git as binaries. They appear to be platform specific compilations of a JNI interface for LLVM, just the kind of thing one would need to rebuild when trying to patch LLVM. As it is, there seems to be no way to do so. Looks to me like a large breach in the open-source-ness of RoboVM.

Darkyenus commented 9 years ago

I have indeed run into the infamous "LLVM ERROR: ran out of registers during register allocation" problem. This error however only seems to happen on compiling scala.concurrent.forkjoin.ForkJoinPool. So, as a workaround, I run whole classpath through the proguard plugin first, to shed unused classes, that somehow get into RoboVM compilation. This works (as long as you don't use said class). The problem with this approach is, that - for some reason I have not yet looked into - it invalidates all robovm cache, so most of the project libraries have to recompile every time, which on my machine results in 30sec compile times.

So we must wait for RoboVM to fix this issue.

As for your fixing attempts, I'd direct those questions directly at RoboVM. I am pretty sure they'll appreciate someone fixing this issue and will be able to help you further. Good luck!

windemut commented 9 years ago

Good to know that this is not just me being stupid. My understanding was that ForkJoinPool was referenced by some other classes which in turn are referenced by Predef, which would explain what I was seeing. Could you provide some details on the workaround, please? I could really use one while the LLVM fix is working itself through the dependency pipeline. Once I understand it, I'd be happy to help work it into the sbt-robovm source, if people like.

I don't think I can really help with the bug itself, the correct solution is for RoboVM to move to LLVM 3.5. They are aware of the problem, but lack urgency because it seems to affect Scala, only. Luckily, the LLVM bug also affects a certain ATI Radeon graphics driver, with a sizable segment of the gaming community providing the motivation to get it fixed in LLVM 3.5. Word is it has been, but I believe it when I see it.

I am also slightly concerned about the other messages ("phantom class"), which I have no explanation for and fear they might be a completely different problem expressing itself...

Darkyenus commented 9 years ago

I will try to post example workaround into example repository as soon as possible. As for the phantom class things... I remember seeing them too, but I can't remember what I did with them, sorry. I'd not worry about it as long as everything works.

windemut commented 9 years ago

Thanks!

If I understand correctly, you have been successful in writing substantial Scala code and running it on the IOS simulator using this workaround. This is great to know. I can't wait to try it myself!

Have you also run it on an actual device?

Darkyenus commented 9 years ago

Unfortunately not yet, because I didn't have paid the developers fee yet, but I am optimistic. I know that it is possible though, so I hope it will work.

Darkyenus commented 9 years ago

For example on how to set up proguard with RoboVM, look here: https://github.com/Darkyenus/scala-ios-demos

@ajhager: It is not cleanly integrated with official examples, so I haven't made a pull request yet. It is mergable, but I haven't updated previous examples yet.

About this solution

I do not know which version of sbt-robovm is published, but this is made to fit the latest version. So you may need to compile and local-publish sbt-robovm yourself. Relevant code is in project/Build.scala, bottom half and important parts are commented. To run, use something like $ sbt proguarded/iphoneSim.

windemut commented 9 years ago

This looks great! Thanks for the fast response. I'll give it a try tonight when I am back at my Mac and report back.

windemut commented 9 years ago

On the device, from what I have heard, this issue only occurs for the x86 architecture, so I share your optimism.

Darkyenus commented 9 years ago

Even if it did happen, this solution should still work. But hopefully, they'll fix it soon

windemut commented 9 years ago

I just tried your code, it works well. I was also able to change the code and verify the result. As you said, though, between Proguard and compiling half the classes, it is quite slow turning around, and will only get slower as we add more code. Still, until they get around to LLVM 3.5, this is great for a proof of principle!

If I end out making any useful modifications to sbt-robovm or scala-ios-demos, I'll be sure to offer it up here. I'm closing this issue for now, as it appears the workaround is working.

Thanks so much for your help!

Darkyenus commented 9 years ago

I believe it would be possible to somehow recompile only changed classes, still using this method. Also, as a dirty workaround, you could remove problematic classes directly from scala library jars and use them. That would make it a bit more unsafe, because with this approach, you can be sure that they are not used.

Proguard is still a valid option for deployment though, because it makes the executable smaller.

If anybody knows how does robovm determine which classes to recompile, I could probably speed up this workaround significantly and maybe integrate into sbt-robovm, for convenience.

ajhager commented 9 years ago

I am indeed running into this as I started porting over the RoboVM samples to Scala. Thanks for both of your help. I will investigate better solutions.

ajhager commented 9 years ago

@Darkyenus I have temporarily adapted your proguard task to the samples. I will attempt to make that process faster.

Darkyenus commented 9 years ago

So, I had time to work on my project, and managed to get some progress. First off: WARNING Using proguard may cause libGDX robovm projects to not take any (touch) input. Since I was not able to fix it and keep using proguard, I'll just say that it is caused by it. It has worked before some time, so conditions are unclear, perhaps some updates to robovm broke it. Just in case somebody has the same problem.

Semi-Good news Unfortunately the problem still isn't fixed, but I have managed to create speed up this workaround, by using custom class-filtering code in Build file, instead of proguard. This means that jars are not merged and RoboVM can use proper caching. It still sometimes compiles more than necessary, but generally, it seems to work a lot better. I don't know how does RoboVM determine what to compile, so I don't know how to optimize it even more, it is a bit strange behavior. Anyway, the code: https://github.com/Darkyenus/scala-ios-demos

I hope it helps! Any information on how does RoboVM determine what to compile would be beneficial.

ajhager commented 9 years ago

This is excellent work! I had put work on this stuff on hold because the Proguard solution was so slow. I will take a look at your work around today. Thanks.

Darkyenus commented 9 years ago

In case someone would want to look into it before I get to it, the code responsible for determining what has to be compiled seems to be here: https://github.com/robovm/robovm/blob/master/compiler/src/main/java/org/robovm/compiler/ClassCompiler.java#L225 (method mustCompile).

@ajhager I think that this could be integrated into the plugin, maybe in some general form (customisable cache location and filtered classes? Maybe only latter). What do you think? (Probably removing Guava dependency first.)

ntherning commented 9 years ago

@windemut please comment on issue robovm/robovm#313 and let us know of the patch you wanted to try. We'd like to take a look at it. Or you can try to apply it yourself. The sources for the LLVM binaries are fully open-sourced and the libs can be built using the build.sh script in the llvm/ dir.

windemut commented 9 years ago

@ntherning: I don't have any patch at this time. I have been able to make Darkyenus' workaround work for me, filed "Scala on iOS" under "doable" as a strategic matter, and proceeded with work unrelated to deployment.

I am very impressed with RoboVM, and when it comes time for deployment, sbt-robovm is going to be my first choice. If and when I do make any improvements to the code, I will make sure to contribute them.

ntherning commented 9 years ago

We've now upgraded to LLVM 3.5 (robovm/robovm#554) and we're using penryn as the CPU variant when building for x86/x86_64 (robovm/robovm#576). These combined I think will resolve the issue with Scala apps (roboscala/sbt-robovm#26). Please try with the latest nightly build of RoboVM and let us know.

Also, we now have x86_64 support in the iOS simulator. You should be able to run Scala apps in 64-bit mode in the simulator to work around this issue.

ntherning commented 9 years ago

We just released a new version of RoboVM which is based on LLVM trunk and uses penryn as CPU when building for x86/x86_64. I believe that this will take care of the problem we have had when compiling x86 (32-bit) apps. Please try with the beta-02 release without the workaround or give me instructions on how to test this myself. Thanks!

Darkyenus commented 9 years ago

Compiling ForkJoinPool now (beta-02) seems to work. Tested using "ios x86 release" configuration. Thank you very much!