Complete cross-compilation support.
authorLudovic Courtès <ludo@gnu.org>
Mon, 21 Nov 2011 21:08:22 +0000 (22:08 +0100)
committerLudovic Courtès <ludo@gnu.org>
Mon, 21 Nov 2011 23:22:46 +0000 (00:22 +0100)
* module/system/base/target.scm (%target-endianness, %target-word-size):
  New fluids.
  (%native-word-size): New variable.
  (with-target): Set these fluids.
  (cpu-endianness, cpu-word-size, triplet-cpu, triplet-vendor,
  triplet-os): New procedures.
  (target-cpu, target-vendor, target-os): Use them.
  (target-endianness, target-word-size): Refer to the corresponding
  fluid.

* libguile/objcodes.c (target_endianness_var, target_word_size_var): New
  global variables.
  (NATIVE_ENDIANNESS): New macro.
  (target_endianness, target_word_size, to_native_order): New functions.
  (make_objcode_from_file): Use `scm_bytecode_to_native_objcode' instead
  of `scm_bytecode_to_objcode'.
  (bytecode_to_objcode): New function, based on `scm_bytecode_to_objcode',
  with the addition of an `endianness' and `word_size' parameters.
  (scm_bytecode_to_objcode): Use it.
  (scm_bytecode_to_native_objcode): New function.
  (scm_write_objcode): Use `target_word_size' and `target_endianness'.
  Convert OBJCODE's len and meta-len to native byte order.
  (scm_init_objcodes): Initialize `target_endianness_var' and
  `target_word_size_var'.

* libguile/objcodes.h (scm_bytecode_to_native_objcode): New declaration.

* libguile/vm.c (really_make_boot_program): Use
  `scm_bytecode_to_native_objcode' instead of `scm_bytecode_to_objcode'.

* test-suite/tests/asm-to-bytecode.test (%objcode-cookie-size): New
  variable.
  (test-target): New procedure.
  ("cross-compilation"): Add `test-target' calls and the "unknown
  target" test.

libguile/objcodes.c
libguile/objcodes.h
libguile/vm.c
module/system/base/target.scm
test-suite/tests/asm-to-bytecode.test

index 6223362..536094f 100644 (file)
@@ -32,6 +32,7 @@
 #include <sys/types.h>
 #include <assert.h>
 #include <alignof.h>
+#include <byteswap.h>
 
 #include <full-read.h>
 
    The length of the header must be a multiple of 8 bytes.  */
 verify (((sizeof (SCM_OBJCODE_COOKIE) - 1) & 7) == 0);
 
+/* Endianness and word size of the compilation target.  */
+static SCM target_endianness_var = SCM_BOOL_F;
+static SCM target_word_size_var = SCM_BOOL_F;
+
 \f
 /*
  * Objcode type
  */
 
+/* Endianness of the build machine.  */
+#ifdef WORDS_BIGENDIAN
+# define NATIVE_ENDIANNESS 'B'
+#else
+# define NATIVE_ENDIANNESS 'L'
+#endif
+
+/* Return the endianness of the compilation target.  */
+static char
+target_endianness (void)
+{
+  if (scm_is_true (target_endianness_var))
+    return scm_is_eq (scm_call_0 (scm_variable_ref (target_endianness_var)),
+                     scm_endianness_big) ? 'B' : 'L';
+  else
+    return NATIVE_ENDIANNESS;
+}
+
+/* Return the word size in bytes of the compilation target.  */
+static size_t
+target_word_size (void)
+{
+  if (scm_is_true (target_word_size_var))
+    return scm_to_size_t (scm_call_0
+                         (scm_variable_ref (target_word_size_var)));
+  else
+    return sizeof (void *);
+}
+
+/* Convert X, which is in byte order ENDIANNESS, to its native
+   representation.  */
+static inline uint32_t
+to_native_order (uint32_t x, char endianness)
+{
+  if (endianness == NATIVE_ENDIANNESS)
+    return x;
+  else
+    return bswap_32 (x);
+}
+
 static void
 verify_cookie (char *cookie, struct stat *st, int map_fd, void *map_addr)
 #define FUNC_NAME "make_objcode_from_file"
@@ -183,7 +228,7 @@ make_objcode_from_file (int fd)
 
     verify_cookie (cookie, &st, -1, NULL);
 
-    return scm_bytecode_to_objcode (bv);
+    return scm_bytecode_to_native_objcode (bv);
   }
 #endif
 }
@@ -254,12 +299,12 @@ SCM_DEFINE (scm_objcode_meta, "objcode-meta", 1, 0, 0,
 }
 #undef FUNC_NAME
 
-SCM_DEFINE (scm_bytecode_to_objcode, "bytecode->objcode", 1, 0, 0,
-           (SCM bytecode),
-           "")
-#define FUNC_NAME s_scm_bytecode_to_objcode
+/* Turn BYTECODE into objcode encoded for ENDIANNESS and WORD_SIZE.  */
+static SCM
+bytecode_to_objcode (SCM bytecode, char endianness, size_t word_size)
+#define FUNC_NAME "bytecode->objcode"
 {
-  size_t size;
+  size_t size, len, metalen;
   const scm_t_uint8 *c_bytecode;
   struct scm_objcode *data;
 
@@ -268,14 +313,17 @@ SCM_DEFINE (scm_bytecode_to_objcode, "bytecode->objcode", 1, 0, 0,
 
   size = SCM_BYTEVECTOR_LENGTH (bytecode);
   c_bytecode = (const scm_t_uint8*)SCM_BYTEVECTOR_CONTENTS (bytecode);
-  
+
   SCM_ASSERT_RANGE (0, bytecode, size >= sizeof(struct scm_objcode));
   data = (struct scm_objcode*)c_bytecode;
 
-  if (data->len + data->metalen != (size - sizeof (*data)))
+  len = to_native_order (data->len, endianness);
+  metalen = to_native_order (data->metalen, endianness);
+
+  if (len + metalen != (size - sizeof (*data)))
     scm_misc_error (FUNC_NAME, "bad bytevector size (~a != ~a)",
                    scm_list_2 (scm_from_size_t (size),
-                               scm_from_uint32 (sizeof (*data) + data->len + data->metalen)));
+                               scm_from_uint32 (sizeof (*data) + len + metalen)));
 
   /* foolishly, we assume that as long as bytecode is around, that c_bytecode
      will be of the same length; perhaps a bad assumption? */
@@ -284,6 +332,27 @@ SCM_DEFINE (scm_bytecode_to_objcode, "bytecode->objcode", 1, 0, 0,
 }
 #undef FUNC_NAME
 
+SCM_DEFINE (scm_bytecode_to_objcode, "bytecode->objcode", 1, 0, 0,
+           (SCM bytecode),
+           "")
+#define FUNC_NAME s_scm_bytecode_to_objcode
+{
+  /* Assume we're called from Scheme, which known that to do with
+     `target-type'.  */
+  return bytecode_to_objcode (bytecode, target_endianness (),
+                             target_word_size ());
+}
+#undef FUNC_NAME
+
+/* Like `bytecode->objcode', but ignore the `target-type' fluid.  This
+   is useful for native compilation that happens lazily---e.g., direct
+   calls to this function from libguile itself.  */
+SCM
+scm_bytecode_to_native_objcode (SCM bytecode)
+{
+  return bytecode_to_objcode (bytecode, NATIVE_ENDIANNESS, sizeof (void *));
+}
+
 SCM_DEFINE (scm_load_objcode, "load-objcode", 1, 0, 0,
            (SCM file),
            "")
@@ -327,40 +396,36 @@ SCM_DEFINE (scm_write_objcode, "write-objcode", 2, 0, 0,
            "")
 #define FUNC_NAME s_scm_write_objcode
 {
-  static SCM target_endianness_var = SCM_BOOL_F;
-  static SCM target_word_size_var = SCM_BOOL_F;
-
   char cookie[sizeof (SCM_OBJCODE_COOKIE) - 1];
-  char endianness;
-  char word_size;
+  char endianness, word_size;
+  size_t total_size;
 
   SCM_VALIDATE_OBJCODE (1, objcode);
   SCM_VALIDATE_OUTPUT_PORT (2, port);
-  
-  if (scm_is_false (target_endianness_var))
-    target_endianness_var =
-      scm_c_public_variable ("system base target", "target-endianness");
-  if (scm_is_false (target_word_size_var))
-    target_word_size_var =
-      scm_c_public_variable ("system base target", "target-word-size");
-
-  endianness = 
-    scm_is_eq (scm_call_0 (scm_variable_ref (target_endianness_var)),
-               scm_endianness_big) ? 'B' : 'L';
-  switch (scm_to_int (scm_call_0 (scm_variable_ref (target_word_size_var))))
+  endianness = target_endianness ();
+  switch (target_word_size ())
     {
-    case 4: word_size = '4'; break;
-    case 8: word_size = '8'; break;
-    default: abort ();
+    case 4:
+      word_size = '4';
+      break;
+    case 8:
+      word_size = '8';
+      break;
+    default:
+      abort ();
     }
 
   memcpy (cookie, SCM_OBJCODE_COOKIE, strlen (SCM_OBJCODE_COOKIE));
   cookie[SCM_OBJCODE_ENDIANNESS_OFFSET] = endianness;
   cookie[SCM_OBJCODE_WORD_SIZE_OFFSET] = word_size;
 
+  total_size =
+    to_native_order (SCM_OBJCODE_LEN (objcode), target_endianness ())
+    + to_native_order (SCM_OBJCODE_META_LEN (objcode), target_endianness ());
+
   scm_c_write (port, cookie, strlen (SCM_OBJCODE_COOKIE));
   scm_c_write (port, SCM_OBJCODE_DATA (objcode),
-               sizeof (struct scm_objcode) + SCM_OBJCODE_TOTAL_LEN (objcode));
+               sizeof (struct scm_objcode) + total_size);
 
   return SCM_UNSPECIFIED;
 }
@@ -400,6 +465,11 @@ scm_init_objcodes (void)
 
   scm_c_define ("word-size", scm_from_size_t (sizeof(SCM)));
   scm_c_define ("byte-order", scm_from_uint16 (SCM_BYTE_ORDER));
+
+  target_endianness_var = scm_c_public_variable ("system base target",
+                                                "target-endianness");
+  target_word_size_var = scm_c_public_variable ("system base target",
+                                               "target-word-size");
 }
 
 /*
index 2fc43d5..925d32f 100644 (file)
@@ -65,6 +65,7 @@ SCM_API SCM scm_load_objcode (SCM file);
 SCM_API SCM scm_objcode_p (SCM obj);
 SCM_API SCM scm_objcode_meta (SCM objcode);
 SCM_API SCM scm_bytecode_to_objcode (SCM bytecode);
+SCM_INTERNAL SCM scm_bytecode_to_native_objcode (SCM bytecode);
 SCM_API SCM scm_objcode_to_bytecode (SCM objcode);
 SCM_API SCM scm_write_objcode (SCM objcode, SCM port);
 
index 6cb85b7..49df5cb 100644 (file)
@@ -392,7 +392,7 @@ really_make_boot_program (long nargs)
 
   u8vec = scm_c_take_gc_bytevector ((scm_t_int8*)bp,
                                     sizeof (struct scm_objcode) + sizeof (text));
-  ret = scm_make_program (scm_bytecode_to_objcode (u8vec),
+  ret = scm_make_program (scm_bytecode_to_native_objcode (u8vec),
                           SCM_BOOL_F, SCM_BOOL_F);
   SCM_SET_CELL_WORD_0 (ret, SCM_CELL_WORD_0 (ret) | SCM_F_PROGRAM_IS_BOOT);
 
index 68c92d8..80d80f3 100644 (file)
@@ -21,6 +21,7 @@
 
 (define-module (system base target)
   #:use-module (rnrs bytevectors)
+  #:use-module (ice-9 regex)
   #:export (target-type with-target
 
             target-cpu target-vendor target-os
 ;;;
 
 (define %target-type (make-fluid))
+(define %target-endianness (make-fluid))
+(define %target-word-size (make-fluid))
 
-(define (target-type)
-  (or (fluid-ref %target-type)
-      %host-type))
+(define %native-word-size
+  ;; The native word size.  Note: don't use `word-size' from
+  ;; (system vm objcode) to avoid a circular dependency.
+  ((@ (system foreign) sizeof) '*))
 
 (define (validate-target target)
   (if (or (not (string? target))
 
 (define (with-target target thunk)
   (validate-target target)
-  (with-fluids ((%target-type target))
-    (thunk)))
+  (let ((cpu (triplet-cpu target)))
+    (with-fluids ((%target-type target)
+                  (%target-endianness (cpu-endianness cpu))
+                  (%target-word-size (cpu-word-size cpu)))
+      (thunk))))
+
+(define (cpu-endianness cpu)
+  "Return the endianness for CPU."
+  (if (string=? cpu (triplet-cpu %host-type))
+      (native-endianness)
+      (cond ((string-match "^i[0-9]86$" cpu)
+             (endianness little))
+            ((member cpu '("x86_64" "ia64"
+                           "powerpcle" "powerpc64le" "mipsel" "mips64el"))
+             (endianness little))
+            ((member cpu '("sparc" "sparc64" "powerpc" "powerpc64" "spu"
+                           "mips" "mips64"))
+             (endianness big))
+            ((string-match "^arm.*el" cpu)
+             (endianness little))
+            (else
+             (error "unknown CPU endianness" cpu)))))
+
+(define (cpu-word-size cpu)
+  "Return the word size for CPU."
+  (if (string=? cpu (triplet-cpu %host-type))
+      %native-word-size
+      (cond ((string-match "^i[0-9]86$" cpu) 4)
+            ((string-match "64$" cpu) 8)
+            ((string-match "64[lbe][lbe]$" cpu) 8)
+            ((member cpu '("sparc" "powerpc" "mips")) 4)
+            ((string-match "^arm.*" cpu) 4)
+            (else "unknown CPU word size" cpu))))
+
+(define (triplet-cpu t)
+  (substring t 0 (string-index t #\-)))
+
+(define (triplet-vendor t)
+  (let ((start (1+ (string-index t #\-))))
+    (substring t start (string-index t #\- start))))
+
+(define (triplet-os t)
+  (let ((start (1+ (string-index t #\- (1+ (string-index t #\-))))))
+    (substring t start)))
+
+\f
+(define (target-type)
+  "Return the GNU configuration triplet of the target platform."
+  (or (fluid-ref %target-type)
+      %host-type))
 
 (define (target-cpu)
-  (let ((t (target-type)))
-    (substring t 0 (string-index t #\-))))
+  "Return the CPU name of the target platform."
+  (triplet-cpu (target-type)))
 
 (define (target-vendor)
-  (let* ((t (target-type))
-         (start (1+ (string-index t #\-))))
-    (substring t start (string-index t #\- start))))
+  "Return the vendor name of the target platform."
+  (triplet-vendor (target-type)))
 
 (define (target-os)
-  (let* ((t (target-type))
-         (start (1+ (string-index t #\- (1+ (string-index t #\-))))))
-    (substring t start)))
+  "Return the operating system name of the target platform."
+  (triplet-os (target-type)))
 
 (define (target-endianness)
-  (if (equal? (target-type) %host-type)
-      (native-endianness)
-      (error "cross-compilation not yet handled" %host-type (target-type))))
+  "Return the endianness object of the target platform."
+  (or (fluid-ref %target-endianness) (native-endianness)))
 
 (define (target-word-size)
-  (if (equal? (target-type) %host-type)
-      ((@ (system foreign) sizeof) '*)
-      (error "cross-compilation not yet handled" %host-type (target-type))))
+  "Return the word size, in bytes, of the target platform."
+  (or (fluid-ref %target-word-size) %native-word-size))
index c2b9cce..edb9bfd 100644 (file)
@@ -21,6 +21,7 @@
   #:use-module ((rnrs io ports) #:select (open-bytevector-output-port))
   #:use-module (test-suite lib)
   #:use-module (system vm instruction)
+  #:use-module (system vm objcode)
   #:use-module (system base target)
   #:use-module (language assembly)
   #:use-module (language assembly compile-bytecode))
                (string=? (target-vendor) vendor)
                (string=? (target-os) os)))))))
 
+(define %objcode-cookie-size
+  (string-length "GOOF----LE-8-2.0"))
+
+(define (test-target triplet endian word-size)
+  (pass-if (format #f "target `~a' honored" triplet)
+    (call-with-values (lambda ()
+                        (open-bytevector-output-port))
+      (lambda (p get-objcode)
+        (with-target triplet
+          (lambda ()
+            (let ((b (compile-bytecode
+                      '(load-program () 16 #f
+                                     (assert-nargs-ee/locals 1)
+                                     (make-int8 77)
+                                     (toplevel-ref 1)
+                                     (local-ref 0)
+                                     (mul)
+                                     (add)
+                                     (return)
+                                     (nop) (nop) (nop)
+                                     (nop) (nop))
+                      #f)))
+              (write-objcode (bytecode->objcode b) p)
+              (let ((cookie   (make-bytevector %objcode-cookie-size))
+                    (expected (format #f "GOOF----~a-~a-~a"
+                                      (cond ((eq? endian (endianness little))
+                                             "LE")
+                                            ((eq? endian (endianness big))
+                                             "BE")
+                                            (else
+                                             (error "unknown endianness"
+                                                    endian)))
+                                      word-size
+                                      (effective-version))))
+                (bytevector-copy! (get-objcode) 0 cookie 0
+                                  %objcode-cookie-size)
+                (string=? (utf8->string cookie) expected)))))))))
+
 (with-test-prefix "cross-compilation"
 
   (test-triplet "i586" "pc" "gnu0.3")
   (test-triplet "x86_64" "unknown" "linux-gnu")
-  (test-triplet "x86_64" "unknown" "kfreebsd-gnu"))
+  (test-triplet "x86_64" "unknown" "kfreebsd-gnu")
+
+  (test-target "i586-pc-gnu0.3" (endianness little) 4)
+  (test-target "x86_64-pc-linux-gnu" (endianness little) 8)
+  (test-target "powerpc-unknown-linux-gnu" (endianness big) 4)
+  (test-target "sparc64-unknown-freebsd8.2" (endianness big) 8)
+
+  (pass-if-exception "unknown target"
+    exception:miscellaneous-error
+    (call-with-values (lambda ()
+                        (open-bytevector-output-port))
+      (lambda (p get-objcode)
+        (let* ((b (compile-bytecode '(load-program () 3 #f
+                                                   (make-int8 77)
+                                                   (return))
+                                    #f))
+               (o (bytecode->objcode b)))
+          (with-target "fcpu-unknown-gnu1.0"
+            (lambda ()
+              (write-objcode o p))))))))
 
 ;; Local Variables:
 ;; eval: (put 'with-target 'scheme-indent-function 1)