Andy, is it OK to use your Perl 5 static inline probing code in MoarVM?

The MoarVM licence is Artistic 2, which I don't think is directly compatible
with Perl 5's union of Artistic & GPL. So I think I need to ask permission
from you, as author and therefore copyright holder.


Attached is the proposed patch, which implements compiler based probing for
the MoarVM Configure.pl. Tested on OS X, FreeBSD, Linux, HP/UX and AIX.
Not tested on Win32, so might not work there without tweaks.

It takes the probing C code for static inline from Perl 5's Configure
pretty much verbatim, and uses that to determine what to use to get the
compiler to generate inline functions that don't also have an external
definition. I believe that probing is essential, as the correct magic to
use varies with different versions of some compilers, and so attempting to
maintain an explicit list is going to be more work than this.

Nicholas Clark
>From dba2746602e470cd325907b0aaedb46970ee96a6 Mon Sep 17 00:00:00 2001
From: Nicholas Clark <n...@ccl4.org>
Date: Tue, 18 Feb 2014 15:13:10 +0100
Subject: [PATCH 1/6] Add probing code to Configure.pm to learn how the
 compiler does 'static inline'

The compiler probing framework is intended to be generic, and is based on the
approach used by Perl 5's Configure, but with the compiler and linker flags
derived from Configure.pl's own values, which should make it portable to Win32
as well as *nix.

The probing code for static inline is a direct port of the Perl 5 code written
by Andy Dougherty, applied to Perl 5 as commit 17a6c8e38505fd8d in July 2010.
---
 Configure.pl   |   3 +
 build/probe.pm | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 178 insertions(+)
 create mode 100644 build/probe.pm

diff --git a/Configure.pl b/Configure.pl
index 0c8b59e..16172f9 100644
--- a/Configure.pl
+++ b/Configure.pl
@@ -11,6 +11,7 @@ use File::Spec;
 
 use build::setup;
 use build::auto;
+use build::probe;
 
 my $NAME    = 'moar';
 my $GENLIST = 'build/gen.list';
@@ -201,9 +202,11 @@ print "OK\n";
 
 if ($config{crossconf}) {
     build::auto::detect_cross(\%config, \%defaults);
+    build::probe::static_inline_cross(\%config, \%defaults);
 }
 else {
     build::auto::detect_native(\%config, \%defaults);
+    build::probe::static_inline_native(\%config, \%defaults);
 }
 
 my $order = $config{be} ? 'big endian' : 'little endian';
diff --git a/build/probe.pm b/build/probe.pm
new file mode 100644
index 0000000..5e4cee2
--- /dev/null
+++ b/build/probe.pm
@@ -0,0 +1,175 @@
+package build::probe;
+use strict;
+use warnings;
+
+use File::Path qw(make_path remove_tree);
+use File::Spec::Functions qw(curdir catdir rel2abs devnull);
+
+my $devnull = devnull();
+
+my $probe_dir;
+
+END {
+    remove_tree($probe_dir)
+        if defined $probe_dir;
+}
+
+{
+    package build::probe::restore_cwd;
+    use Cwd;
+
+    sub new {
+        my $cwd = getcwd;
+        die "Can't getcwd: $!"
+            unless defined $cwd && length $cwd;
+        bless \$cwd;
+    }
+
+    sub DESTROY {
+        chdir ${$_[0]}
+            or warn "Can't restore cwd to ${$_[0]}: $!";
+    }
+}
+
+sub _to_probe_dir {
+    unless (defined $probe_dir) {
+        $probe_dir = rel2abs(catdir(curdir(), 'probe'));
+        make_path($probe_dir);
+    }
+    my $restore = build::probe::restore_cwd->new();
+    chdir $probe_dir
+        or die "Can't chir $probe_dir: $!";
+    return $restore;
+}
+    
+sub compile {
+    my ($config, $leaf, $defines, $files) = @_;
+    my $restore = _to_probe_dir();
+
+    my $cl_define = join ' ', map {$config->{ccdef} . $_} @$defines;
+
+    my @objs;
+    foreach my $file ("$leaf.c", @$files) {
+        (my $obj = $file) =~ s/\.c/$config->{obj}/;
+        my $command = "$config->{cc} $cl_define $config->{ccout} $obj $config->{ccswitch} $file >$devnull 2>&1";
+        system $command
+            and return;
+        push @objs, $obj;
+    }
+
+    my $command = "$config->{ld} $config->{ldout} $leaf @objs  >$devnull 2>&1";
+    system $command
+        and return;
+    return 1;
+}
+
+sub _spew {
+    my ($filename, $content) = @_;
+    open my $fh, '>', $filename
+        or die "Can't open $filename: $!";
+    print $fh $content
+        or die "Can't write to $filename: $!";
+    close $fh
+        or die "Can't close $filename: $!";
+}
+
+sub static_inline_native {
+    my ($config) = @_;
+    my $restore = _to_probe_dir();
+    _spew('try.c', <<'EOT');
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+#ifdef __GNUC__
+     return EXIT_SUCCESS;
+#else
+     return EXIT_FAILURE;
+#endif
+}
+EOT
+
+    print ::dots('    probing whether your compiler thinks that it is gcc');
+    compile($config, 'try')
+        or die "Can't compile simple gcc probe, so something is badly wrong";
+    my $gcc = !system './try';
+    print $gcc ? "YES\n": "NO\n";
+
+    print ::dots('    probing how your compiler does static inline');
+
+    _spew('inline.c', <<'EOCP');
+#include <stdlib.h>
+extern int f_via_a(int x);
+extern int f_via_b(int x);
+int main(int argc, char **argv)
+{
+    int y;
+
+    y = f_via_a(0);
+#ifdef USE_B
+    y = f_via_b(0);
+#endif
+    if (y == 42) {
+        return EXIT_SUCCESS;
+    }
+    else {
+        return EXIT_FAILURE;
+    }
+}
+EOCP
+
+_spew('a.c', <<'EOCP');
+static INLINE int f(int x) {
+    int y;
+    y = x + 42;
+    return y;
+}
+
+int f_via_a(int x)
+{
+    return f(x);
+}
+EOCP
+_spew('b.c', <<'EOCP');
+extern int f(int x);
+
+int f_via_b(int x)
+{
+    return f(x);
+}
+EOCP
+
+    # For gcc, prefer __inline__, which permits the cflags to add -ansi
+    my @try = $gcc ? qw(__inline__ inline __inline _inline)
+        : qw(inline __inline__ __inline _inline);
+
+    my $s_i;
+    while (my $try = shift @try) {
+        next unless compile($config, 'inline', ["INLINE=$try"], ['a.c']);
+        next if system "./inline";
+        # Now make sure there is no external linkage of static functions
+        if(!compile($config, 'inline', ["INLINE=$try", "USE_B"], ['a.c', 'b.c'])
+           || system "./inline") {
+            $s_i = "static $try";
+            last;
+        }
+    }
+
+    if ($s_i) {
+        print "$s_i\n";
+    } else {
+        print "none, so falling back to static\n";
+        $s_i = 'static';
+    }
+    $config->{static_inline} = $s_i;
+}
+
+sub static_inline_cross {
+    my ($config) = @_;
+    # FIXME. Needs testing, but might be robust enough to do what the native
+    # code does, but just skip the system() to run the executable. Although this
+    # might get confused by link time optimisations that only fail at run time,
+    # which the system test does detect.
+    $config->{static_inline} = 'static';
+}
+
+'00';
-- 
1.8.4.2

Reply via email to