From d6d2bda05b31736ec74f090aa3c3adbd329ad70f Mon Sep 17 00:00:00 2001
From: Zbynek Slajchrt <zbynek.slajchrt@oracle.com>
Date: Fri, 2 Mar 2018 18:18:09 +0100
Subject: [PATCH] Dynamic linking of native libs with LLVM libs

---
 .../ffi/impl/common/JavaUpCallsRFFIImpl.java  | 166 +++++++++---------
 .../r/ffi/impl/interop/package-info.java      |   6 +-
 .../r/ffi/impl/llvm/TruffleLLVM_Context.java  |   4 -
 .../r/ffi/impl/llvm/TruffleLLVM_DLL.java      |  92 ++++++++--
 .../r/ffi/impl/llvm/tools/ShowLLVMIR.java     |   6 +-
 .../upcalls/BytesToNativeCharArrayCallMR.java |   2 +-
 .../ffi/impl/nodes/NativeStringCastNode.java  |  22 +++
 com.oracle.truffle.r.native/Makefile          |  17 +-
 com.oracle.truffle.r.native/fficall/Makefile  |   7 +-
 .../fficall/src/truffle_llvm/Memory.c         |   3 +-
 .../gnur/patch/src/library/Makefile           |   5 +-
 .../gnur/patch/src/library/stats/Makefile     |   3 +-
 .../llvm_tools/llvm-c++                       |   2 +-
 .../llvm_tools/llvm-cc                        |   2 +-
 .../llvm_tools/llvm-helper                    |  46 +++--
 .../r/runtime/data/NativeDataAccess.java      |  48 ++++-
 .../truffle/r/runtime/data/RObject.java       |  16 --
 .../truffle/r/runtime/data/RPairList.java     |   4 +-
 18 files changed, 280 insertions(+), 171 deletions(-)

diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
index 3f524ddfaa..523b80e3b1 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java
@@ -906,9 +906,9 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     }
 
     /**
-     * Helper function for {@code R_TopLevelExec} which is similar to {@code R_TryEval} except that a C
-     * function is invoked (in the native layer) instead of an R expression. assert: this is ONLY called
-     * from R_TopLevelExec prior to calling C function.
+     * Helper function for {@code R_TopLevelExec} which is similar to {@code R_TryEval} except that
+     * a C function is invoked (in the native layer) instead of an R expression. assert: this is
+     * ONLY called from R_TopLevelExec prior to calling C function.
      */
     @Override
     @TruffleBoundary
@@ -1826,101 +1826,101 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
                 }
             });
         }
+    }
 
-        @MessageResolution(receiverType = VectorWrapper.class)
-        public static class VectorWrapperMR {
+    @MessageResolution(receiverType = VectorWrapper.class)
+    public static class VectorWrapperMR {
 
-            @Resolve(message = "IS_POINTER")
-            public abstract static class IntVectorWrapperNativeIsPointerNode extends Node {
-                protected Object access(@SuppressWarnings("unused") VectorWrapper receiver) {
-                    return false;
-                }
+        @Resolve(message = "IS_POINTER")
+        public abstract static class IntVectorWrapperNativeIsPointerNode extends Node {
+            protected Object access(@SuppressWarnings("unused") VectorWrapper receiver) {
+                return false;
             }
+        }
 
-            @Resolve(message = "TO_NATIVE")
-            public abstract static class IntVectorWrapperNativeAsPointerNode extends Node {
-                protected Object access(VectorWrapper receiver) {
-                    return new VectorWrapperNativePointer(receiver.vector);
-                }
+        @Resolve(message = "TO_NATIVE")
+        public abstract static class IntVectorWrapperNativeAsPointerNode extends Node {
+            protected Object access(VectorWrapper receiver) {
+                return new VectorWrapperNativePointer(receiver.vector);
             }
+        }
 
-            @Resolve(message = "HAS_SIZE")
-            public abstract static class VectorWrapperHasSizeNode extends Node {
-                protected Object access(@SuppressWarnings("unused") VectorWrapper receiver) {
-                    return true;
-                }
+        @Resolve(message = "HAS_SIZE")
+        public abstract static class VectorWrapperHasSizeNode extends Node {
+            protected Object access(@SuppressWarnings("unused") VectorWrapper receiver) {
+                return true;
             }
+        }
 
-            @Resolve(message = "GET_SIZE")
-            public abstract static class VectorWrapperGetSizeNode extends Node {
-                @Child private Node getSizeMsg = Message.GET_SIZE.createNode();
+        @Resolve(message = "GET_SIZE")
+        public abstract static class VectorWrapperGetSizeNode extends Node {
+            @Child private Node getSizeMsg = Message.GET_SIZE.createNode();
 
-                protected Object access(VectorWrapper receiver) {
-                    try {
-                        return ForeignAccess.sendGetSize(getSizeMsg, receiver.vector);
-                    } catch (UnsupportedMessageException e) {
-                        throw RInternalError.shouldNotReachHere(e);
-                    }
+            protected Object access(VectorWrapper receiver) {
+                try {
+                    return ForeignAccess.sendGetSize(getSizeMsg, receiver.vector);
+                } catch (UnsupportedMessageException e) {
+                    throw RInternalError.shouldNotReachHere(e);
                 }
             }
+        }
 
-            @Resolve(message = "READ")
-            abstract static class VectorWrapperReadNode extends Node {
-                @Child private Node readMsg = Message.READ.createNode();
+        @Resolve(message = "READ")
+        abstract static class VectorWrapperReadNode extends Node {
+            @Child private Node readMsg = Message.READ.createNode();
 
-                public Object access(VectorWrapper receiver, Object index) {
-                    try {
-                        return ForeignAccess.sendRead(readMsg, receiver.vector, index);
-                    } catch (UnsupportedMessageException | UnknownIdentifierException e) {
-                        throw RInternalError.shouldNotReachHere(e);
-                    }
+            public Object access(VectorWrapper receiver, Object index) {
+                try {
+                    return ForeignAccess.sendRead(readMsg, receiver.vector, index);
+                } catch (UnsupportedMessageException | UnknownIdentifierException e) {
+                    throw RInternalError.shouldNotReachHere(e);
                 }
             }
+        }
 
-            @Resolve(message = "WRITE")
-            abstract static class VectorWrapperWriteNode extends Node {
-                @Child private Node writeMsg = Message.WRITE.createNode();
+        @Resolve(message = "WRITE")
+        abstract static class VectorWrapperWriteNode extends Node {
+            @Child private Node writeMsg = Message.WRITE.createNode();
 
-                public Object access(VectorWrapper receiver, Object index, Object value) {
-                    try {
-                        return ForeignAccess.sendWrite(writeMsg, receiver.vector, index, value);
-                    } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException e) {
-                        throw RInternalError.shouldNotReachHere(e);
-                    }
+            public Object access(VectorWrapper receiver, Object index, Object value) {
+                try {
+                    return ForeignAccess.sendWrite(writeMsg, receiver.vector, index, value);
+                } catch (UnsupportedMessageException | UnknownIdentifierException | UnsupportedTypeException e) {
+                    throw RInternalError.shouldNotReachHere(e);
                 }
             }
+        }
 
-            @Resolve(message = "IS_EXECUTABLE")
-            abstract static class VectorWrapperIsExecutableNode extends Node {
-                @Child private Node isExecMsg = Message.IS_EXECUTABLE.createNode();
+        @Resolve(message = "IS_EXECUTABLE")
+        abstract static class VectorWrapperIsExecutableNode extends Node {
+            @Child private Node isExecMsg = Message.IS_EXECUTABLE.createNode();
 
-                public Object access(VectorWrapper receiver) {
-                    return ForeignAccess.sendIsExecutable(isExecMsg, receiver.vector);
-                }
+            public Object access(VectorWrapper receiver) {
+                return ForeignAccess.sendIsExecutable(isExecMsg, receiver.vector);
             }
+        }
 
-            @Resolve(message = "EXECUTE")
-            abstract static class VectorWrapperExecuteNode extends Node {
-                @Child private Node execMsg = Message.createExecute(0).createNode();
-
-                protected Object access(VectorWrapper receiver, Object[] arguments) {
-                    try {
-                        // Currently, there is only one "executable" object, which is
-                        // CharSXPWrapper.
-                        // See CharSXPWrapperMR for the EXECUTABLE message handler.
-                        assert arguments.length == 0 && receiver.vector instanceof CharSXPWrapper;
-                        return ForeignAccess.sendExecute(execMsg, receiver.vector);
-                    } catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
-                        throw RInternalError.shouldNotReachHere(e);
-                    }
+        @Resolve(message = "EXECUTE")
+        abstract static class VectorWrapperExecuteNode extends Node {
+            @Child private Node execMsg = Message.createExecute(0).createNode();
+
+            protected Object access(VectorWrapper receiver, Object[] arguments) {
+                try {
+                    // Currently, there is only one "executable" object, which is
+                    // CharSXPWrapper.
+                    // See CharSXPWrapperMR for the EXECUTABLE message handler.
+                    assert arguments.length == 0 && receiver.vector instanceof CharSXPWrapper;
+                    return ForeignAccess.sendExecute(execMsg, receiver.vector);
+                } catch (UnsupportedMessageException | UnsupportedTypeException | ArityException e) {
+                    throw RInternalError.shouldNotReachHere(e);
                 }
             }
+        }
 
-            @CanResolve
-            public abstract static class VectorWrapperCheck extends Node {
-                protected static boolean test(TruffleObject receiver) {
-                    return receiver instanceof VectorWrapper;
-                }
+        @CanResolve
+        public abstract static class VectorWrapperCheck extends Node {
+            protected static boolean test(TruffleObject receiver) {
+                return receiver instanceof VectorWrapper;
             }
         }
     }
@@ -1930,19 +1930,20 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
         private final TruffleObject vector;
 
         public VectorWrapper(TruffleObject vector) {
+            assert vector instanceof RObject;
             this.vector = vector;
-            ((RObject) vector).setNativeWrapper(this);
+            NativeDataAccess.setNativeWrapper((RObject) vector, this);
         }
 
         static Object get(TruffleObject x) {
             assert x instanceof RObject;
-            Object wrapper = ((RObject) x).getNativeWrapper();
+            Object wrapper = NativeDataAccess.getNativeWrapper((RObject) x);
             if (wrapper != null) {
                 return wrapper;
             } else {
                 wrapper = new VectorWrapper(x);
                 // Establish the 1-1 relationship between the object and its native wrapper
-                ((RObject) x).setNativeWrapper(wrapper);
+                NativeDataAccess.setNativeWrapper((RObject) x, wrapper);
                 return wrapper;
             }
         }
@@ -1966,12 +1967,8 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
     @Override
     public Object INTEGER(Object x) {
         // also handles LOGICAL
-        assert x instanceof RIntVector || x instanceof RLogicalVector || x == RNull.instance || x instanceof RUnboundValue : "" + x + ":" + x.getClass();
-        if (x instanceof RUnboundValue) {
-            return VectorWrapper.get((RUnboundValue) x);
-        } else {
-            return VectorWrapper.get(guaranteeVectorOrNull(x, RVector.class));
-        }
+        assert x instanceof RIntVector || x instanceof RLogicalVector || x == RNull.instance;
+        return VectorWrapper.get(guaranteeVectorOrNull(x, RVector.class));
     }
 
     @Override
@@ -1996,12 +1993,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI {
 
     @Override
     public Object R_CHAR(Object x) {
-        TruffleObject xto = guaranteeVectorOrNull(x, CharSXPWrapper.class);
-        if (xto == RNull.instance) {
-            return xto;
-        }
-        CharSXPWrapper chw = (CharSXPWrapper) xto;
-        return VectorWrapper.get(chw);
+        return VectorWrapper.get(guaranteeVectorOrNull(x, CharSXPWrapper.class));
     }
 
     @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java
index 41e4198d9e..fc661a180f 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/interop/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,9 +23,5 @@
 /**
  * A collection of types and {@link com.oracle.truffle.api.interop.MessageResolution} classes that
  * support the implementations.
- *
- * See com.oracle.truffle.r.ffi.impl.interop.base and com.oracle.truffle.r.ffi.impl.interop.pcre for
- * similar classes specific to the {@link com.oracle.truffle.r.runtime.ffi.BaseRFFI} and
- * {@link com.oracle.truffle.r.runtime.ffi.PCRERFFI} interfaces.
  */
 package com.oracle.truffle.r.ffi.impl.interop;
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
index 87b27410ab..65330af2da 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_Context.java
@@ -86,10 +86,6 @@ final class TruffleLLVM_Context extends RFFIContext {
 
         String libzPath = LibPaths.getBuiltinLibPath("z");
         TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create().getCallTarget().call(libzPath, false, true);
-
-        // String curlPath = LibPaths.getBuiltinLibPath("curl.4");
-        String curlPath = "libcurl.so";
-        TruffleLLVM_NativeDLL.NativeDLOpenRootNode.create().getCallTarget().call(curlPath, false, true);
     }
 
     @Override
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
index a1ba42a713..f934408376 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DLL.java
@@ -23,14 +23,22 @@
 package com.oracle.truffle.r.ffi.impl.llvm;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.nio.file.FileSystems;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
@@ -39,6 +47,8 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RPlatform;
+import com.oracle.truffle.r.runtime.RPlatform.OSInfo;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.context.RContext.ContextState;
 import com.oracle.truffle.r.runtime.ffi.DLL;
@@ -63,12 +73,24 @@ import com.oracle.truffle.r.runtime.ffi.DLLRFFI;
  *
  */
 public class TruffleLLVM_DLL implements DLLRFFI {
+    /*
+     * The LIBS file, which is included in the LLVM archive, enumerates native dynamic libraries to
+     * be linked with the LLVM library in the archive.
+     */
+    private static final String LIBS = "LIBS";
+
+    private static final Set<String> ignoredNativeLibs = new HashSet<>();
+    static {
+        ignoredNativeLibs.add("Rblas");
+        ignoredNativeLibs.add("Rlapack");
+    }
+
     static class ContextStateImpl implements RContext.ContextState {
         /**
-         * When a new {@link RContext} is created we have to re-parse the libR modules, unfortunately, as
-         * there is no way to propagate the LLVM state created in the initial context. TODO when do we
-         * really need to do this? This is certainly too early for contexts that will not invoke LLVM code
-         * (e.g. most unit tests)
+         * When a new {@link RContext} is created we have to re-parse the libR modules,
+         * unfortunately, as there is no way to propagate the LLVM state created in the initial
+         * context. TODO when do we really need to do this? This is certainly too early for contexts
+         * that will not invoke LLVM code (e.g. most unit tests)
          */
         @Override
         public ContextState initialize(RContext context) {
@@ -121,7 +143,19 @@ public class TruffleLLVM_DLL implements DLLRFFI {
         boolean match(String name);
     }
 
-    public static LLVM_IR[] getZipLLVMIR(String path) {
+    public static final class LLVMArchive {
+        public final LLVM_IR[] irs;
+        public final List<String> nativeLibs;
+
+        private LLVMArchive(LLVM_IR[] irs, List<String> nativeLibs) {
+            super();
+            this.irs = irs;
+            this.nativeLibs = nativeLibs;
+        }
+    }
+
+    public static LLVMArchive getZipLLVMIR(String path) {
+        List<String> nativeLibs = Collections.emptyList();
         try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(path)))) {
             ArrayList<LLVM_IR> irList = new ArrayList<>();
             while (true) {
@@ -130,24 +164,28 @@ public class TruffleLLVM_DLL implements DLLRFFI {
                     break;
                 }
                 int size = (int) entry.getSize();
-                byte[] bc = new byte[size];
+                byte[] bytes = new byte[size];
                 int n;
                 int totalRead = 0;
-                while (totalRead < size && (n = zis.read(bc, totalRead, size - totalRead)) != -1) {
+                while (totalRead < size && (n = zis.read(bytes, totalRead, size - totalRead)) != -1) {
                     totalRead += n;
                 }
+                if (LIBS.equals(entry.getName())) {
+                    nativeLibs = getNativeLibs(bytes);
+                    continue;
+                }
                 Path zipName = Paths.get(entry.getName());
                 String name = zipName.getFileName().toString();
                 int ix = name.indexOf('.');
                 if (ix > 0) {
                     name = name.substring(0, ix);
                 }
-                LLVM_IR.Binary ir = new LLVM_IR.Binary(name, bc);
+                LLVM_IR.Binary ir = new LLVM_IR.Binary(name, bytes);
                 irList.add(ir);
                 // debugging
                 if (System.getenv("FASTR_LLVM_DEBUG") != null) {
                     try (FileOutputStream bs = new FileOutputStream(Paths.get("tmpzip", name).toString())) {
-                        bs.write(bc);
+                        bs.write(bytes);
                     }
                     try (PrintStream bs = new PrintStream(new FileOutputStream(Paths.get("tmpb64", name).toString()))) {
                         bs.print(ir.base64);
@@ -156,25 +194,38 @@ public class TruffleLLVM_DLL implements DLLRFFI {
             }
             LLVM_IR[] result = new LLVM_IR[irList.size()];
             irList.toArray(result);
-            return result;
+            return new LLVMArchive(result, nativeLibs);
         } catch (IOException ex) {
             // not a zip file
             return null;
         }
     }
 
+    private static List<String> getNativeLibs(byte[] bytes) throws IOException {
+        List<String> libs = new LinkedList<>();
+        try (BufferedReader libReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(bytes)))) {
+            String lib = null;
+            while ((lib = libReader.readLine()) != null) {
+                libs.add(lib);
+            }
+        }
+        return libs;
+    }
+
     private static class TruffleLLVM_DLOpenNode extends Node implements DLOpenNode {
         @Child private TruffleLLVM_NativeDLL.TruffleLLVM_NativeDLOpen nativeDLLOpenNode;
 
         /**
-         * If a library is enabled for LLVM, the IR for all the modules is retrieved and analyzed. Every
-         * exported symbol in the module added to the parseStatus map for the current {@link RContext}. This
-         * allows {@code dlsym} to definitively locate any symbol, even if the IR has not been parsed yet.
+         * If a library is enabled for LLVM, the IR for all the modules is retrieved and analyzed.
+         * Every exported symbol in the module added to the parseStatus map for the current
+         * {@link RContext}. This allows {@code dlsym} to definitively locate any symbol, even if
+         * the IR has not been parsed yet.
          */
         @Override
         public Object execute(String path, boolean local, boolean now) {
             try {
-                LLVM_IR[] irs = getZipLLVMIR(path);
+                LLVMArchive ar = getZipLLVMIR(path);
+                LLVM_IR[] irs = ar.irs;
                 if (irs == null) {
                     return tryOpenNative(path, local, now);
                 }
@@ -182,6 +233,8 @@ public class TruffleLLVM_DLL implements DLLRFFI {
                 if (libName.equals("libR")) {
                     // save for new RContexts
                     truffleDLL.libRModules = irs;
+                } else {
+                    loadNativeLibs(ar.nativeLibs);
                 }
                 for (LLVM_IR ir : irs) {
                     parseLLVM(libName, ir);
@@ -203,6 +256,17 @@ public class TruffleLLVM_DLL implements DLLRFFI {
             }
         }
 
+        private void loadNativeLibs(List<String> nativeLibs) {
+            OSInfo osInfo = RPlatform.getOSInfo();
+            for (String nativeLib : nativeLibs) {
+                if (ignoredNativeLibs.contains(nativeLib)) {
+                    continue;
+                }
+                String nativeLibName = "lib" + nativeLib + "." + osInfo.libExt;
+                tryOpenNative(nativeLibName, false, true);
+            }
+        }
+
         private long tryOpenNative(String path, boolean local, boolean now) throws UnsatisfiedLinkError {
             if (nativeDLLOpenNode == null) {
                 nativeDLLOpenNode = insert(new TruffleLLVM_NativeDLL.TruffleLLVM_NativeDLOpen());
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/tools/ShowLLVMIR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/tools/ShowLLVMIR.java
index 91acc85892..a606291213 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/tools/ShowLLVMIR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/tools/ShowLLVMIR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@ import java.io.OutputStream;
 
 import com.oracle.truffle.r.ffi.impl.llvm.LLVM_IR;
 import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_DLL;
+import com.oracle.truffle.r.ffi.impl.llvm.TruffleLLVM_DLL.LLVMArchive;
 import com.oracle.truffle.r.runtime.ProcessOutputManager;
 
 public class ShowLLVMIR {
@@ -64,7 +65,8 @@ public class ShowLLVMIR {
             usage();
         }
         try {
-            LLVM_IR[] irs = TruffleLLVM_DLL.getZipLLVMIR(objPath);
+            LLVMArchive ar = TruffleLLVM_DLL.getZipLLVMIR(objPath);
+            LLVM_IR[] irs = ar.irs;
             if (irs == null) {
                 System.out.printf("no llvm ir in %s\n", objPath);
                 System.exit(1);
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/upcalls/BytesToNativeCharArrayCallMR.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/upcalls/BytesToNativeCharArrayCallMR.java
index ebefcef623..96e7f6e18b 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/upcalls/BytesToNativeCharArrayCallMR.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/upcalls/BytesToNativeCharArrayCallMR.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/NativeStringCastNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/NativeStringCastNode.java
index 4c7f70db2c..92d7e73417 100644
--- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/NativeStringCastNode.java
+++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/NativeStringCastNode.java
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
 package com.oracle.truffle.r.ffi.impl.nodes;
 
 import static com.oracle.truffle.r.runtime.data.NativeDataAccess.readNativeString;
diff --git a/com.oracle.truffle.r.native/Makefile b/com.oracle.truffle.r.native/Makefile
index 514fd0857f..159c2720d3 100644
--- a/com.oracle.truffle.r.native/Makefile
+++ b/com.oracle.truffle.r.native/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -21,7 +21,7 @@
 # questions.
 #
 
-.PHONY: all clean 
+.PHONY: all clean
 
 export TOPDIR = $(CURDIR)
 export FASTR_R_HOME=$(abspath $(TOPDIR)/..)
@@ -30,6 +30,15 @@ export FASTR_NATIVE_DIR = $(TOPDIR)
 export R_VERSION = 3.4.0
 export GNUR_HOME = $(TOPDIR)/gnur/patch-build
 
+ifeq ($(FASTR_RFFI),llvm)
+ifndef FASTR_LLVM_TOOLS
+ifdef FASTR_LLVM_FOR_DRAGONEGG_HOME
+export FASTR_LLVM_TOOLS = $(FASTR_LLVM_FOR_DRAGONEGG_HOME)/bin
+$(info FASTR_LLVM_TOOLS set to $(FASTR_LLVM_TOOLS))
+endif
+endif
+endif
+
 $(info R_VERSION: $(R_VERSION))
 $(info GNUR_HOME: $(GNUR_HOME))
 
@@ -43,7 +52,7 @@ $(info GNUR_HOME_BINARY not set. Assuming the default location at $(GNUR_HOME_BI
 endif
 
 # Completely accurate dependency analysis is very difficult for this project, so use a version number
-# to force a clean build, and elsewhere use sentinels to avoid rebuilding when we can't compute the 
+# to force a clean build, and elsewhere use sentinels to avoid rebuilding when we can't compute the
 # dependencies accurately.
 
 all: checkversion
@@ -54,7 +63,7 @@ all: checkversion
 	$(MAKE) -C run
 	cp version.source version.built
 
-clean: 
+clean:
 	$(MAKE) -C include clean
 	$(MAKE) -C fficall clean
 	$(MAKE) -C run clean
diff --git a/com.oracle.truffle.r.native/fficall/Makefile b/com.oracle.truffle.r.native/fficall/Makefile
index e22efe39d8..e6e0b6a202 100644
--- a/com.oracle.truffle.r.native/fficall/Makefile
+++ b/com.oracle.truffle.r.native/fficall/Makefile
@@ -80,12 +80,7 @@ fficall.done: common.done
 
 else
 ifeq ($(FASTR_RFFI),llvm)
-ifndef FASTR_LLVM_TOOLS
-ifdef FASTR_LLVM_FOR_DRAGONEGG_HOME
-export FASTR_LLVM_TOOLS = $(FASTR_LLVM_FOR_DRAGONEGG_HOME)/bin
-$(info FASTR_LLVM_TOOLS set to $(FASTR_LLVM_TOOLS))
-endif
-endif
+export R_PACKAGE_DIR="lib"
 fficall.done: common.done
 	$(MAKE) -C src/truffle_llvm all
 	touch fficall.done
diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c
index 680d13aaad..734c99bdd7 100644
--- a/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c
+++ b/com.oracle.truffle.r.native/fficall/src/truffle_llvm/Memory.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1995-2015, The R Core Team
  * Copyright (c) 2003, The R Foundation
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates
  *
  * All rights reserved.
  */
@@ -120,4 +120,3 @@ SEXP Rf_allocS4Object() {
 	unimplemented("Rf_allocS4Object unimplemented");
 	return NULL;
 }
-
diff --git a/com.oracle.truffle.r.native/gnur/patch/src/library/Makefile b/com.oracle.truffle.r.native/gnur/patch/src/library/Makefile
index 7c53c00be0..6861f1ebc7 100644
--- a/com.oracle.truffle.r.native/gnur/patch/src/library/Makefile
+++ b/com.oracle.truffle.r.native/gnur/patch/src/library/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 SUBDIRS = base compiler datasets utils grDevices graphics grid parallel splines stats stats4 methods tools
 export FASTR_LIBRARY_DIR = $(abspath $(TOPDIR)/../library)
+export R_PACKAGE_DIR="."
 
 all: libdir make_subdirs
 
@@ -39,6 +40,6 @@ clean_subdirs:
 	for dir in $(SUBDIRS); do \
 		$(MAKE) PACKAGE=$$dir -C $$dir clean || exit 1; \
 	done
-	
+
 libdir:
 	mkdir -p $(FASTR_LIBRARY_DIR)
diff --git a/com.oracle.truffle.r.native/gnur/patch/src/library/stats/Makefile b/com.oracle.truffle.r.native/gnur/patch/src/library/stats/Makefile
index 20981356dc..ed0b9d2ac4 100644
--- a/com.oracle.truffle.r.native/gnur/patch/src/library/stats/Makefile
+++ b/com.oracle.truffle.r.native/gnur/patch/src/library/stats/Makefile
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -33,5 +33,4 @@ endif
 
 PKG_LIBS := $(LAPACK_LIBS) $(BLAS_LIBS) -L$(FASTR_LIB_DIR) $(FLIBS)
 PKG_INCLUDES = -I src
-
 include ../lib.mk
diff --git a/com.oracle.truffle.r.native/llvm_tools/llvm-c++ b/com.oracle.truffle.r.native/llvm_tools/llvm-c++
index a20027b69b..ec93f97e3d 100755
--- a/com.oracle.truffle.r.native/llvm_tools/llvm-c++
+++ b/com.oracle.truffle.r.native/llvm_tools/llvm-c++
@@ -33,7 +33,7 @@ if [ $is_link -eq 1 ]
 then
   extraObj=""
   if [ -f "$R_PACKAGE_DIR/libobjects" ]; then
-     extraObj=`cat $R_PACKAGE_DIR/libobjects`
+     extraObj=`cat "$R_PACKAGE_DIR/libobjects"`
   fi
   create_bc_lib $@ $extraObj
 else
diff --git a/com.oracle.truffle.r.native/llvm_tools/llvm-cc b/com.oracle.truffle.r.native/llvm_tools/llvm-cc
index 1ab1691aaa..42acf7a327 100755
--- a/com.oracle.truffle.r.native/llvm_tools/llvm-cc
+++ b/com.oracle.truffle.r.native/llvm_tools/llvm-cc
@@ -33,7 +33,7 @@ if [ $is_link -eq 1 ]
 then
   extraObj=""
   if [ -f "$R_PACKAGE_DIR/libobjects" ]; then
-     extraObj=`cat $R_PACKAGE_DIR/libobjects`
+     extraObj=`cat "$R_PACKAGE_DIR/libobjects"`
   fi
   create_bc_lib $@ $extraObj
 else
diff --git a/com.oracle.truffle.r.native/llvm_tools/llvm-helper b/com.oracle.truffle.r.native/llvm_tools/llvm-helper
index 88d41309ff..2f06ecefc7 100644
--- a/com.oracle.truffle.r.native/llvm_tools/llvm-helper
+++ b/com.oracle.truffle.r.native/llvm_tools/llvm-helper
@@ -132,25 +132,39 @@ function fake_obj() {
 function create_bc_lib() {
   bcfiles=""
   lib=""
+  statlibs="$R_PACKAGE_DIR/statlibs"
+  linkedLibs="LIBS"
+  > $linkedLibs
   while [[ $# -gt 0 ]]
   do
     case $1 in
       -o)
         shift
-		lib=$1
-      ;;
+		    lib=$1
+        ;;
+      -l*)
+		    linkedLib=`echo $1 | cut -c 3-`
+        statLibFound='0'
+        if [ -f "$statlibs" ]; then
+          statLibFound=`cat "$statlibs" | grep "lib${linkedLib}.a" | wc -l`
+        fi
+        if [ $statLibFound == '0' ]
+		    then
+          echo $linkedLib >> $linkedLibs
+		    fi
+        ;;
       -*)
-        # ignore options
-      ;;
-     *)
+        # ignore other options
+        ;;
+      *)
         f=$(basename $1)
-     	ext=${f##*.}
-		if [ $ext == 'o' ]
-		then
-	  		fn="$(dirname $1)/${f%%.*}.bc"
-			bcfiles+="$fn "
-		fi
-     ;;
+     	  ext=${f##*.}
+		    if [ $ext == 'o' ]
+		    then
+	  		   fn="$(dirname $1)/${f%%.*}.bc"
+			     bcfiles+="$fn "
+		    fi
+        ;;
     esac
     shift
   done
@@ -161,8 +175,9 @@ function create_bc_lib() {
 # link the bitcode files into a single one using llvm-link before zipping it
   llvm_tool=llvm-link
   get_llvm_tool
+  echo "Linking $lib.bc from LLVM modules: $bcfiles"
   runit $llvm_tool_bin $bcfiles -o $lib.bc
-  runit zip -r $lib $lib.bc
+  runit zip -r $lib $lib.bc $linkedLibs
   rm $lib.bc
 }
 
@@ -220,5 +235,8 @@ function create_bc_archive() {
 
   # Join the archived objects CSV with the unique symbols objects CSV to get the list of objects to be statically linked to the package dynamic library.
   # Append the result to the global list of extra object files.
-  join -t " " -1 1 -2 1 $archObjCSV $exportedObjCSV | awk -F ' ' '{print $2}' >> $R_PACKAGE_DIR/libobjects
+  join -t " " -1 1 -2 1 $archObjCSV $exportedObjCSV | awk -F ' ' '{print $2}' >> "$R_PACKAGE_DIR/libobjects"
+
+  # Record the archive (static library) name to distinguish between LLVM static libs and native libs when linking (via -l)
+  echo $archname >> "$R_PACKAGE_DIR/statlibs"
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java
index 3b404e84df..09b82c05c4 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/NativeDataAccess.java
@@ -114,6 +114,16 @@ public final class NativeDataAccess {
          */
         private long length;
 
+        /**
+         * It maintains the <code>1-?</code> relationship between this object and its native wrapper
+         * through which the native code accesses it. For instance, Sulong implements the "pointer"
+         * equality of two objects that are not pointers (i.e. <code>IS_POINTER</code> returns
+         * <code>false</code>) as the reference equality of the objects. It follows that the pointer
+         * comparison would fail if the same <code>RObject</code> instance were wrapped by two
+         * different native wrappers.
+         */
+        private Object nativeWrapper;
+
         NativeMirror() {
             this.id = counter.incrementAndGet();
         }
@@ -204,8 +214,8 @@ public final class NativeDataAccess {
         if (arg instanceof RObject) {
             RObject obj = (RObject) arg;
             NativeMirror mirror = (NativeMirror) obj.getNativeMirror();
-            if (mirror == null) {
-                mirror = putMirrorObject(arg, obj);
+            if (mirror == null || mirror.id == 0) {
+                mirror = putMirrorObject(arg, obj, mirror);
             }
             return mirror.id;
         }
@@ -213,16 +223,19 @@ public final class NativeDataAccess {
     }
 
     @TruffleBoundary
-    private static NativeMirror putMirrorObject(Object arg, RObject obj) {
-        NativeMirror mirror;
-        obj.setNativeMirror(mirror = arg instanceof CustomNativeMirror ? new NativeMirror(((CustomNativeMirror) arg).getCustomMirrorAddress()) : new NativeMirror());
+    private static NativeMirror putMirrorObject(Object arg, RObject obj, NativeMirror oldMirror) {
+        NativeMirror newMirror;
+        obj.setNativeMirror(newMirror = arg instanceof CustomNativeMirror ? new NativeMirror(((CustomNativeMirror) arg).getCustomMirrorAddress()) : new NativeMirror());
+        if (oldMirror != null) {
+            newMirror.nativeWrapper = oldMirror.nativeWrapper;
+        }
         // System.out.println(String.format("adding %16x = %s", mirror.id,
         // obj.getClass().getSimpleName()));
-        nativeMirrors.put(mirror.id, new WeakReference<>(obj));
+        nativeMirrors.put(newMirror.id, new WeakReference<>(obj));
         if (TRACE_MIRROR_ALLOCATION_SITES) {
-            registerAllocationSite(arg, mirror);
+            registerAllocationSite(arg, newMirror);
         }
-        return mirror;
+        return newMirror;
     }
 
     @TruffleBoundary
@@ -692,4 +705,23 @@ public final class NativeDataAccess {
         mirror.length = length;
 
     }
+
+    public static void setNativeWrapper(RObject obj, Object wrapper) {
+        NativeMirror mirror = (NativeMirror) obj.getNativeMirror();
+        if (mirror == null) {
+            mirror = new NativeMirror(0);
+            obj.setNativeMirror(mirror);
+        }
+        mirror.nativeWrapper = wrapper;
+    }
+
+    public static Object getNativeWrapper(RObject obj) {
+        NativeMirror mirror = (NativeMirror) obj.getNativeMirror();
+        if (mirror == null) {
+            return null;
+        } else {
+            return mirror.nativeWrapper;
+        }
+    }
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObject.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObject.java
index c4f8cdc520..d80f67f4bf 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObject.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObject.java
@@ -25,15 +25,6 @@ package com.oracle.truffle.r.runtime.data;
 public abstract class RObject {
 
     private Object nativeMirror;
-    /**
-     * It maintains the <code>1-?</code> relationship between this object and its native wrapper
-     * through which the native code accesses it. For instance, Sulong implements the "pointer"
-     * equality of two objects that are not pointers (i.e. <code>IS_POINTER</code> returns
-     * <code>false</code>) as the reference equality of the objects. It follows that the pointer
-     * comparison would fail if the same <code>RObject</code> instance were wrapped by two different
-     * native wrappers.
-     */
-    private Object nativeWrapper;
 
     public final void setNativeMirror(Object mirror) {
         this.nativeMirror = mirror;
@@ -43,11 +34,4 @@ public abstract class RObject {
         return nativeMirror;
     }
 
-    public void setNativeWrapper(Object wrapper) {
-        this.nativeWrapper = wrapper;
-    }
-
-    public Object getNativeWrapper() {
-        return this.nativeWrapper;
-    }
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
index 86be2ec8d8..8ba6f70a6a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
@@ -48,8 +48,8 @@ public final class RPairList extends RSharingAttributeStorage implements RAbstra
     private Object car = RNull.instance;
     private Object cdr = RNull.instance;
     /**
-     * Externally, i.e., when serialized, this is either a SYMSXP ({@link RSymbol}) or an {@link RNull}.
-     * Internally it may take on other, non-null, values.
+     * Externally, i.e., when serialized, this is either a SYMSXP ({@link RSymbol}) or an
+     * {@link RNull}. Internally it may take on other, non-null, values.
      */
     private Object tag = RNull.instance;
 
-- 
GitLab