Skip to content

Commit

Permalink
Fix target triple for real iOS device
Browse files Browse the repository at this point in the history
  • Loading branch information
light-tech committed Oct 30, 2020
1 parent 9f06ad1 commit e088647
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 19 deletions.
57 changes: 38 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@ Build LLVM for iOS (physical device and simulator)
From [the official instructions](https://llvm.org/docs/GettingStarted.html):

```shell
git clone https://github.com/llvm/llvm-project.git # Alternatively, download and extract the monorepo source code from https://releases.llvm.org/download.html
# Alternative to git clone is to download and extract the monorepo source code from https://releases.llvm.org/download.html
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
mkdir build
cd build
cmake -G <generator> [options] ../llvm
```

Our script [buildllvm-iOS.sh](buildllvm-iOS.sh) and [buildllvm-iOS-Simulator.sh](buildllvm-iOS-Simulator.sh) build LLVM, Clang and LLD for iOS and iOS simulator respectively. We disable various stuffs such as `terminfo` since there is no terminal in iOS; otherwise, there will be problem when linking in Xcode. Needs:
Our script [buildllvm-iOS.sh](buildllvm-iOS.sh) and [buildllvm-iOS-Simulator.sh](buildllvm-iOS-Simulator.sh) build LLVM, Clang, LLD and LibC++ for iOS and iOS simulator respectively.
We disable various stuffs such as `terminfo` since there is no terminal in iOS; otherwise, there will be problem when linking in Xcode.
Needs:
* [Xcode](https://developer.apple.com/xcode/): Download from app store.
* [CMake](https://cmake.org/download/): See [installation instruction](https://tudat.tudelft.nl/installation/setupDevMacOs.html) to add to PATH.
* [Ninja](https://github.com/ninja-build/ninja/releases): Download and extract the ninja executable to `~/Downloads` folder.

Once the tools are ready, run the script in the `llvm-project` top folder (or `llvm-project-VERSION` if you download the source zipped package instead of cloning).
Once the build process is completed, the library and include headers should be available at `~/Download/LLVM-iOS` or `~/Download/LLVM-iOS-Simulator`.

Once the build process is completed, the library and include headers should be installed at `~/Download/LLVM-iOS` or `~/Download/LLVM-iOS-Simulator`.
(We will subsequently refer to these directories as the _LLVM installation dir_.)

Before being able to use in Xcode, in the built folder, we first need to move the `lib/clang/` and `lib/cmake` and `lib/*.dylib` out of `lib/`:
```shell
Expand All @@ -31,13 +36,14 @@ mv lib/cmake lib2/
mv lib/*.dylib lib2/
```
Otherwise, iOS will crash when loading dynamic libraries.
Maybe remove the unnecessary stuffs in `bin` as well.
Running our script [prepare-llvm.sh](prepare-llvm.sh) in the LLVM installation folder i.e. `~/Download/LLVM-iOS` or `~/Download/LLVM-iOS-Simulator` will perform the necessary set-up.
Running our script [prepare-llvm.sh](prepare-llvm.sh) in the LLVM installation dir will perform the necessary set-up.

Optionally, you could move the `liblld*` to `lib2` as well and the `bin` since it's unlikely you need binary linkage and the `clang` command line program in iOS app.

Our Sample iOS Project
----------------------

We provide a sample iOS app project in the [Sample/](Sample) folder; no license attached so feel free to do whatever you want with it.
We provide a sample iOS app project in the [Sample/](Sample) folder; _no license attached_ so feel free to do whatever you want with it.
In this project, we use Clang's C interpreter example located in `examples/clang-interpreter/main.cpp` of Clang source code to interpret a simple C++ program.
(The file was renamed to `Interpreter.cpp` to fit in with iOS development style.)
The code is pretty much copied verbatim except for some minor modifications, namely:
Expand All @@ -48,18 +54,24 @@ The code is pretty much copied verbatim except for some minor modifications, nam
```c++
// llvm::llvm_shutdown();
```
so that you can call `clangInterpret` again in the app. Originally, the example was a one-shot command line program where this makes sense.
so that you can _call `clangInterpret` again_ in the app.
This line only makes sense in the original program because it was a one-shot command line program.

3. We add a third parameter
```c++
llvm::raw_ostream &errorOutputStream
```
to `clangInterpret` and replace all `llvm::errs()` with `errorOutputStream` so we can capture the compilation output and pass it back to the app front-end to display to users.
to `clangInterpret` and replace all `llvm::errs()` with `errorOutputStream` so we can capture the compilation output and pass it back to the app front-end to display to the user.

4. **For real iOS device**: The implementation of [`llvm::sys::getProcessTriple()`](https://github.com/llvm/llvm-project/blob/master/llvm/lib/Support/Host.cpp) is currently bogus according to the implementation of [`JITTargetMachineBuilder::detectHost()`](https://github.com/llvm/llvm-project/blob/master/llvm/lib/ExecutionEngine/Orc/JITTargetMachineBuilder.cpp): _FIXME: getProcessTriple is bogus. It returns the host LLVM was compiled on, rather than a valid triple for the current process._
So we need to add the appropriate conditional compilation directive `#if TARGET_OS_SIMULATOR ... #else ... #endif` to give it the correct triple. (The platform macro is documented at `<TargetConditionals.h>`.)

In the latest version, you should be able to edit the program, interpret it and see the output in the app UI.

In the latest version, you should be able to edit the program, interpret it and see the output.
Before building the project, you need to copy the LLVM installation folder, say `~/Download/LLVM-iOS-Simulator`, to the root folder of the project like this
```shell
cp ~/Download/LLVM-iOS-Simulator LLVM # Assuming at Sample project folder
# At Sample project folder:
cp ~/Download/LLVM-iOS-Simulator LLVM
```
Here, we copy the `LLVM-iOS-Simulator` to build the app and run it on the simulator.

Expand All @@ -68,7 +80,8 @@ Here, we copy the `LLVM-iOS-Simulator` to build the app and run it on the simula

Read on for details on how to create and configure your own project.

**Known Limitation**: For simulator, can only build **Debug** version only! The current app does not work on real device yet!
**Known Limitation**: For simulator, can only build **Debug** version only!
The current app does not work on real device yet (UI is OK but cannot interpret)!

Behind the Scene: Configure iOS App Xcode Project
-------------------------------------------------
Expand All @@ -77,12 +90,7 @@ These days, you probably want to write your app in _Swift_ whereas LLVM library
```
Swift <-> Objective-C <-> C++
```
A typical approach will be using
* Swift: anything iOS-related (UI, file system, Internet, ...)
* Objective-C: simple class say `LLVMBridge` to expose service such as compilation; basically to convert data types between C++ and Swift such as Swift's `Data` to Objective-C's `NSData` to C++'s buffer `char*`.
* C++: actual implementation of processing functionality.

Go to [Further Readings](#further-readings) for more details.
Go to [Further Readings](#further-readings) for more details on Swift-C++ interoperability.

1. Create a new iOS app project in Xcode and copy an LLVM installation to the project folder.
Unfortunately, LLVM cannot build fat binary for iOS at the moment so we have to manually switch between the two LLVM installations when we switch testing between real iOS device (ARM) or iOS simulator (x86_64).
Expand All @@ -102,10 +110,12 @@ Add a new item `$(PROJECT_DIR)/LLVM/include`.
Again, go to **Build settings** your project and search for `bridg` and you should find **Objective-C Bridging Header** under **Swift Compiler - General**.
Set it to `PROJECT_NAME/LLVMBridge.h` or if you are using more than just LLVM, a header file of your choice (but that header should include `LLVMBridge.h`).

**Note**: Only Objective-C classes in *Objective-C Bridging Header* are visible to Swift!

![Objective-C Bridging Header Setting](ObjCBridgeHeader.png)

At this point, we should be able to run the project on iOS simulator.
**To build the app on real iOS device, an extra step is needed.**
**To build the app for real iOS devices, an extra step is needed.**

5. Since we are using a bunch of precompiled static libraries (and not the actual C++ source code in our app), we need to disable bitcode. Search for `bitcod` and set **Enable Bitcode** setting to `No`.

Expand All @@ -116,7 +126,7 @@ Now you are ready to make use of LLVM glory.
Further Readings
----------------

You might want to start with _Anthony Nguyen_'s
To start, you might want to start with _Anthony Nguyen_'s
[Using C++ in Objective-C iOS app: My first walk](https://medium.com/@nguyenminhphuc/using-c-in-objective-c-ios-app-my-first-walk-77319d94a940)
for a quick intro on how to make use of C++ in Objective-C.
(Note that both C++ and Objective-C are extensions of C and reduces to C.)
Expand All @@ -125,5 +135,14 @@ An easy read on Objective-C and Swift interoperability could be found in
by _RDerik_.
Combining these two articles is the basis for our Sample app.

A typical approach to allow C++ in Swift-based iOS app will be using
* _Swift_ : Anything iOS-related (UI, file system access, Internet, ...)
* _Objective-C_ : Simple classes (like `LLVMBridge` in our Sample app) to expose service written in C++.
The main role is to convert data types between C++ and Swift.
For example: Swift's `Data` to Objective-C's `NSData` to C++'s buffer `char*` (and length).
* _C++_ : Actual implementation of processing functionality.

**Tip**: When writing bridging classes, you should use `NSData` for arguments instead of `NSString` and leave the `String <-> Data` conversion to Swift since you will want a `char*` in C++ anyway.

_Apple_'s [Programming with Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210)
is fairly useful in helping us write the Objective-C bridging class `LLVMBridge`: Once we pass to C++, we are in our home turf.
11 changes: 11 additions & 0 deletions Sample/Sample/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
//
//===----------------------------------------------------------------------===//

#include <TargetConditionals.h>

#include "clang/Basic/DiagnosticOptions.h"
#include "clang/CodeGen/CodeGenAction.h"
#include "clang/Driver/Compilation.h"
Expand Down Expand Up @@ -73,9 +75,14 @@ class SimpleJIT {

public:
static Expected<std::unique_ptr<SimpleJIT>> Create() {
#if TARGET_OS_SIMULATOR
auto JTMB = JITTargetMachineBuilder::detectHost();
if (!JTMB)
return JTMB.takeError();
#else
// FIXME: Memory leaks
auto JTMB = new JITTargetMachineBuilder(Triple("arm64-apple-darwin"));
#endif

auto TM = JTMB->createTargetMachine();
if (!TM)
Expand Down Expand Up @@ -129,7 +136,11 @@ int clangInterpret(int argc, const char **argv, llvm::raw_ostream &errorOutputSt
IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);

#if TARGET_OS_SIMULATOR
const std::string TripleStr = llvm::sys::getProcessTriple();
#else
const std::string TripleStr = "arm64-apple-darwin";
#endif
llvm::Triple T(TripleStr);

// Use ELF on Windows-32 and MingW for now.
Expand Down

0 comments on commit e088647

Please sign in to comment.