Wednesday 9 March 2022

GNAT LLVM

This is a note about building GNAT-LLVM on macOS.

There’s an initial note on GNAT-LLVM here.

I initially started building on macOS in January 2020, and got hello.adb to build. I revisited in February 2021, and had trouble here and here, ending up in a build in late March.

Starting again in February 2022, on Darwin 21.3.0 (Monterey) with
* gnat-llvm at commit 053e755 of 25 Feb
* GCC sources at commit f320197 of 14 Feb
* GCC 12.0.1 x86_64-apple-darwin15 built from the same sources
* clang+llvm-13.0.1-x86_64-apple-darwin from llvm.org

I encountered similar problems (clearly I’d forgotten the 2021 trek!). Arnaud’s remarks about exception handling led me to try a much simpler piece of code:

with Ada.Text_IO;

procedure Raiser is
    procedure Inner (N : Natural) is
   begin
      if N <= 5 then
         raise Constraint_Error;
      end if;
      Inner (N - 1);
   end Inner;
begin
   Inner (10);
exception
   when Constraint_Error =>
      Ada.Text_IO.Put_Line ("got it!");
end Raiser;

which sure enough prints out got it!.

Trying with this project file, on the other hand …

project Raiser is
   for Languages use ("Ada", "C++");
   for Source_Files use ("raiser.adb");
   for Main use ("raiser.adb");
end Raiser;

reveals much of interest: the normal bind/link process is
* gprbind
* gnatbind
* compile the generated binding code
* link with gcc

but if gprbuild sees that C++ is involved (even if there aren’t any C++ source files) it uses a C++ compiler to do the final link.

Which C++ compiler? the first C++ compiler that gprconfig sees on the PATH. Normally that’d be g++, but if you put /usr/bin first, it’ll be the Apple clang++.

If g++ is first, the result of running raiser is as before. If /usr/bin/clang++ is first, though, the result is

raised CONSTRAINT_ERROR : raiser.adb:7 explicit raise

You get the same effect if you put the prebuilt clang+llvm’s bin/ before GCC’s.

After thinking about this a bit, the conclusion is that exception unwinding fails to see the exception handler in raiser.adb, and reports the exception as though the handler didn’t exist.

Simples, you say, put GCC before the working clang+llvm.

Unfortunately, you have to use libc++, and the resulting executable generates errors from malloc() about freeing memory that wasn’t allocated.

As a last throw of the dice, let’s try building llvm and clang ourselves. This is in the directory gnat-llvm/llvm. I changed the Makefile to pick up version 13.0.1, and redefined DL from wget to curl -K -O. Now I needed a newer CMake (I had 3.6.2, I found that the newest binaries at cmake.org, e.g. cmake-3.22.3-macos-universal.dmg, wouldn’t install; cmake-3.18.1-Darwin-x86_64.dmg at Github, which I’d used earlier on a different machine, installed fine).

The build went OK, aside from taking 18 hours - maybe I should have said -j0, maybe running on a 2012 Macbook Pro running El Capitan with 4 GB of RAM was pushing it a bit.

Now, setting the new clang and friends on the PATH (after GCC 12.0.1), the new GNAT compiler built OK.

Something did go wrong building the runtime: gprlib couldn’t copy the ALI files to the library directory! I tried twice, but in the end just copied them there by hand.

It turned out that the actual compiler (llvm-gnat1) had been built dynamically, and used 75 .dylibs. 75. I had to copy all of llvm’s .dylibs (130 of them) to a place where llvm-gnat1 was prepared to look for them, $prefix/obj worked.

The new compiler now compiled raiser correctly (I knew it could catch exceptions, because the compiler uses exceptions internally, specifically while compiling the runtime library), also my D'Hondt Calculator.

Now install the compiler on a 2015 Macbook Pro running Monterey - everything fails, even llvm-gnatchop. This is the first time I’ve had that sort of problem, but a rebuild on that machine should fix it. Deep breath …

Ah! adainclude/, as built, holds a link farm to the compiler sources on an external drive. Unplug the drive - no system.ads!

No comments:

Post a Comment