New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FreeBSD/aarch64 support #1904

Open
wants to merge 1 commit into
base: trunk
from

Conversation

Projects
None yet
3 participants
@myfreeweb

myfreeweb commented Jul 15, 2018

Getting OCaml to run on FreeBSD/aarch64 was mostly trivial :) There was an interesting assembler problem though.

Using the clang/llvm assembler avoids an extra dependency on GNU binutils, and that's what 32-bit arm is using:

as="${TOOLPREF}cc -c"

But on aarch64, the following error has occurred:

gmake[4]: Entering directory '/usr/home/greg/ocaml-4.07.0/stdlib'
../boot/ocamlrun ../ocamlopt -strict-sequence -absname -w +a-4-9-41-42-44-45-48 -g -warn-error A -bin-annot -nostdlib -safe-string -strict-formats    \
					 -o stdlib__bytes.cmx -c bytes.ml
../boot/ocamlrun ../ocamlopt -strict-sequence -absname -w +a-4-9-41-42-44-45-48 -g -warn-error A -bin-annot -nostdlib -safe-string -strict-formats    \
					 -o stdlib__array.cmx -c array.ml
../boot/ocamlrun ../ocamlopt -strict-sequence -absname -w +a-4-9-41-42-44-45-48 -g -warn-error A -bin-annot -nostdlib -safe-string -strict-formats    \
					 -o stdlib__set.cmx -c set.ml                                                                                                                
../boot/ocamlrun ../ocamlopt -strict-sequence -absname -w +a-4-9-41-42-44-45-48 -g -warn-error A -bin-annot -nostdlib -safe-string -strict-formats    \
					 -o stdlib__stack.cmx -c stack.ml                                                                                                            
../boot/ocamlrun ../ocamlopt -strict-sequence -absname -w +a-4-9-41-42-44-45-48 -g -warn-error A -bin-annot -nostdlib -safe-string -strict-formats    \
					 -o stdlib__complex.cmx -c complex.ml
/tmp/camlasmb8ca65.s:465:13: error: encoded floating point value out of range
				fmov    d12, #0x3ff0000000000000                                     
											^                                                      
/tmp/camlasmb8ca65.s:488:12: error: encoded floating point value out of range
				fmov    d7, #0x3ff0000000000000                                      
										 ^                                                       
/tmp/camlasmb8ca65.s:638:12: error: encoded floating point value out of range
				fmov    d9, #0x3ff0000000000000                                      
										 ^                                                       
/tmp/camlasmb8ca65.s:645:13: error: encoded floating point value out of range
				fmov    d14, #0x3fe0000000000000                                     
											^                                                      
/tmp/camlasmb8ca65.s:658:13: error: encoded floating point value out of range
				fmov    d20, #0x3ff0000000000000                                     
											^                                                      
/tmp/camlasmb8ca65.s:665:13: error: encoded floating point value out of range
				fmov    d24, #0x3fe0000000000000                                     
											^                                                      
/tmp/camlasmb8ca65.s:690:12: error: encoded floating point value out of range
				fmov    d6, #0x3fe0000000000000
										 ^
/tmp/camlasmb8ca65.s:720:12: error: encoded floating point value out of range
				fmov    d2, #0x3fe0000000000000
										 ^
File "/usr/home/greg/ocaml-4.07.0/stdlib/complex.ml", line 1:
Error: Assembler error, input left in file /tmp/camlasmb8ca65.s
gmake[4]: *** [Makefile:252: stdlib__complex.cmx] Error 2

Turns out LLVM thinks that if floating point immediates are written in hex, they must be integer values between 0 and 255:

  if (Tok.is(AsmToken::Real) || Tok.is(AsmToken::Integer)) {
    int64_t Val;
    if (Tok.is(AsmToken::Integer) && !isNegative && Tok.getString().startswith("0x")) {
      Val = Tok.getIntVal();
      if (Val > 255 || Val < 0) {
        TokError("encoded floating point value out of range");
        return MatchOperand_ParseFail;
      }
    } else {

As a workaround, I turned off the immediates :D I guess a better one would be to print the values as floats e.g. fmov d2, 123.456. How would I do that and could there be any problems with that?

// Other than that: runtime-C-exceptions and runtime-errors tests pass; OPAM compiled successfully (after updating extlib); currently running all tests — everything is fine so far.

// Source for the CONTEXT_PC being gp_elr: this mail.

UPD: 1758 tests passed, 29 tests skipped, 3 tests failed, 88 tests not started

 ... testing 'exec.ml' with 1.2 (native) => failed (Files /usr/home/greg/ocaml/testsuite/_ocamltest/tests/lib-unix/unix-execvpe/exec/ocamlopt.byte/exec.opt and /usr/ho
me/greg/ocaml/testsuite/_ocamltest/tests/lib-unix/unix-execvpe/exec/ocamlopt.opt/exec.opt are different)
 ... testing 'testpreempt.ml' with 1.1 (bytecode) => failed (program output /usr/home/greg/ocaml/testsuite/_ocamltest/tests/lib-systhreads/testpreempt/ocamlc.byte/test
preempt.byte.output differs from reference /usr/home/greg/ocaml/testsuite/tests/lib-systhreads/testpreempt.reference:
--- /usr/home/greg/ocaml/testsuite/tests/lib-systhreads/testpreempt.reference   2018-07-15 01:02:56.471201000 +0300
+++ /usr/home/greg/ocaml/testsuite/_ocamltest/tests/lib-systhreads/testpreempt/ocamlc.byte/testpreempt.byte.output      2018-07-15 03:33:07.554411000 +0300
@@ -1,3 +1,2 @@                                                                   
-Interaction 1                             
-Interaction 2  
 Long computation result: 100000
+Interaction 1                                                    
                                                   
)                                           
 ... testing 'testpreempt.ml' with 1.2 (native) => failed (program output /usr/home/greg/ocaml/testsuite/_ocamltest/tests/lib-systhreads/testpreempt/ocamlopt.byte/test
preempt.opt.output differs from reference /usr/home/greg/ocaml/testsuite/tests/lib-systhreads/testpreempt.reference:
--- /usr/home/greg/ocaml/testsuite/tests/lib-systhreads/testpreempt.reference   2018-07-15 01:02:56.471201000 +0300
+++ /usr/home/greg/ocaml/testsuite/_ocamltest/tests/lib-systhreads/testpreempt/ocamlopt.byte/testpreempt.opt.output     2018-07-15 03:33:13.898101000 +0300
@@ -1,3 +1 @@                                              
-Interaction 1
-Interaction 2
 Long computation result: 100000
                  
)
@shindere

This comment has been minimized.

Show comment
Hide comment
@shindere

shindere Jul 16, 2018

Contributor
Contributor

shindere commented Jul 16, 2018

@stedolan

This comment has been minimized.

Show comment
Hide comment
@stedolan

stedolan Jul 16, 2018

Contributor

Nice!

I'm a little worried by the Config.system = "freebsd" check, though: that's not a great way to detect the LLVM assembler (you can use that assembler on Linux, and you can use the gnu assembler on FreeBSD, so this only detects defaults correctly). I think the workaround you suggest (printing immediates as floats) would be a better idea, and you can implement it by changing the printf format on line 590.

For anyone following along, here's the problem as I understand it: Aarch64 has a fmov instruction for loading floating-point constants that takes an 8-bit immediate, which can represent one of 256 different floating-point numbers using a custom compressed format (sign bit, 3-bit exponent, 4-bit mantissa). However, when parsing an assembler operand expressed in hex, the GNU assembler expects a 64-bit IEEE754 double (which it then checks is one of the 256 values that can be encoded in 8 bits), while the LLVM assembler expects an 8-bit encoded value. However, I think both assemblers agree on how to parse an operand expressed using floating-point syntax.

Contributor

stedolan commented Jul 16, 2018

Nice!

I'm a little worried by the Config.system = "freebsd" check, though: that's not a great way to detect the LLVM assembler (you can use that assembler on Linux, and you can use the gnu assembler on FreeBSD, so this only detects defaults correctly). I think the workaround you suggest (printing immediates as floats) would be a better idea, and you can implement it by changing the printf format on line 590.

For anyone following along, here's the problem as I understand it: Aarch64 has a fmov instruction for loading floating-point constants that takes an 8-bit immediate, which can represent one of 256 different floating-point numbers using a custom compressed format (sign bit, 3-bit exponent, 4-bit mantissa). However, when parsing an assembler operand expressed in hex, the GNU assembler expects a 64-bit IEEE754 double (which it then checks is one of the 256 values that can be encoded in 8 bits), while the LLVM assembler expects an 8-bit encoded value. However, I think both assemblers agree on how to parse an operand expressed using floating-point syntax.

Add FreeBSD/aarch64 support
Using the clang/llvm assembler avoids an extra dependency on GNU binutils, and that's what 32-bit arm is using.
But in this case, there was a problem with floating point immediates: LLVM thinks that if they're written in hex, they must be integer values between 0 and 255.
Changed them to float literals.
@myfreeweb

This comment has been minimized.

Show comment
Hide comment
@myfreeweb

myfreeweb Jul 16, 2018

I was slightly afraid of float literals (parsing incompatibility or something) but I guess IEEE is IEEE :) Using the float literal now.

About continuous integration: QEMU to the rescue! It's not perfect, but seems to work for OCaml — I just built a compiler under it.

Here's how to use it:

pkg install -y qemu-user-static
/usr/sbin/binmiscctl add arm64 --interpreter "/usr/local/bin/qemu-aarch64-static" --magic "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00" --mask "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" --size 20 --set-enabled

wget "https://download.freebsd.org/ftp/snapshots/arm64/12.0-CURRENT/base.txz"
mkdir /tmp/aarch64
cd /tmp/aarch64
tar -xvf /where/you/downloaded/base.txz
cp /etc/resolv.conf etc/
mkdir usr/local/bin
cp /usr/local/bin/qemu-aarch64-static usr/local/bin
mkdir ocaml
mount -t nullfs /some/path/to/ocaml ocaml

chroot .
pkg install -y gmake
cd ocaml
./configure
gmake -j16 world.opt

(Most commands here should be run as root)

(No idea how well a 12-CURRENT qemu chroot would work on an <=11 host, you might want to download an 11.2-STABLE snapshot instead. 12 has 64-bit inodes after all…)

myfreeweb commented Jul 16, 2018

I was slightly afraid of float literals (parsing incompatibility or something) but I guess IEEE is IEEE :) Using the float literal now.

About continuous integration: QEMU to the rescue! It's not perfect, but seems to work for OCaml — I just built a compiler under it.

Here's how to use it:

pkg install -y qemu-user-static
/usr/sbin/binmiscctl add arm64 --interpreter "/usr/local/bin/qemu-aarch64-static" --magic "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00" --mask "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff" --size 20 --set-enabled

wget "https://download.freebsd.org/ftp/snapshots/arm64/12.0-CURRENT/base.txz"
mkdir /tmp/aarch64
cd /tmp/aarch64
tar -xvf /where/you/downloaded/base.txz
cp /etc/resolv.conf etc/
mkdir usr/local/bin
cp /usr/local/bin/qemu-aarch64-static usr/local/bin
mkdir ocaml
mount -t nullfs /some/path/to/ocaml ocaml

chroot .
pkg install -y gmake
cd ocaml
./configure
gmake -j16 world.opt

(Most commands here should be run as root)

(No idea how well a 12-CURRENT qemu chroot would work on an <=11 host, you might want to download an 11.2-STABLE snapshot instead. 12 has 64-bit inodes after all…)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment