From c5acf56a15dfa2c41fe3f1c31273a971eeaf4f40 Mon Sep 17 00:00:00 2001 From: stepan Date: Thu, 23 Jan 2020 11:13:57 +0100 Subject: [PATCH] Use NativeMemory abstraction everywhere --- .gitignore | 2 + .../ffi/impl/common/JavaUpCallsRFFIImpl.java | 11 +- .../llvm/TruffleLLVM_DownCallNodeFactory.java | 6 +- .../r/ffi/impl/nfi/TruffleNFI_Context.java | 36 +- .../nfi/TruffleNFI_DownCallNodeFactory.java | 6 +- .../r/ffi/impl/nfi/TruffleNFI_Utils.java | 8 +- .../r/ffi/impl/nodes/MathFunctionsNodes.java | 216 ++---------- .../r/ffi/impl/nodes/TryRfEvalNode.java | 33 +- .../r/runtime/data/NativeDataAccess.java | 307 +++++++----------- .../truffle/r/runtime/data/RObjectSize.java | 7 +- .../r/runtime/ffi/CRFFIUnwrapVectorNode.java | 4 +- .../r/runtime/ffi/CRFFIWrapVectorNode.java | 4 +- .../truffle/r/runtime/ffi/UnsafeAdapter.java | 45 --- .../r/runtime/ffi/interop/NativeArray.java | 151 ++++++++- .../ffi/interop/NativeArrayExport.java | 81 ----- .../runtime/ffi/interop/NativeCharArray.java | 6 +- .../ffi/interop/NativeDoubleArray.java | 113 ++----- .../ffi/interop/NativeIntegerArray.java | 111 ++----- .../r/runtime/ffi/interop/NativePointer.java | 26 +- .../r/runtime/ffi/interop/NativeRawArray.java | 8 +- .../runtime/ffi/interop/NativeUInt8Array.java | 134 +++----- .../interop}/StringArrayWrapper.java | 27 +- .../r/runtime/ffi/interop/UnsafeAdapter.java | 45 --- .../r/runtime/ffi/util/NativeMemory.java | 163 +++++++++- .../ffi/util/ReadDoublePointerNode.java | 95 ++++++ .../runtime/ffi/util/ReadIntPointerNode.java | 95 ++++++ .../r/runtime/ffi/util/WritePointerNode.java | 95 ++++++ .../r/test/runtime/ffi/NativeArrayTests.java | 160 +++++++++ 28 files changed, 1080 insertions(+), 915 deletions(-) delete mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java delete mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArrayExport.java rename com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/{data => ffi/interop}/StringArrayWrapper.java (81%) delete mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/UnsafeAdapter.java create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadDoublePointerNode.java create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadIntPointerNode.java create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/WritePointerNode.java create mode 100644 com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/runtime/ffi/NativeArrayTests.java diff --git a/.gitignore b/.gitignore index f54b307e80..e5fabed7f8 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ com.oracle.truffle.r.native/run/Makeconf.etc-e /deparse/ /DEPARSE_ERROR /dumps/ +/graal_dumps/ /Rpkgsource/* /install.cran.logs/ /library/ @@ -162,3 +163,4 @@ lib.install.packages.gnur .Rproj.user com.oracle.truffle.r.test.native/packages/*/*/src/*.so com.oracle.truffle.r.test.native/packages/*/*/src/*.o +tags 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 c47da8c876..e9b25bede9 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, 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 @@ -107,8 +107,9 @@ import com.oracle.truffle.r.runtime.ffi.DLL.DotSymbol; import com.oracle.truffle.r.runtime.ffi.DLL.SymbolHandle; import com.oracle.truffle.r.runtime.ffi.FFIMaterializeNode; -import com.oracle.truffle.r.runtime.ffi.UnsafeAdapter; import com.oracle.truffle.r.runtime.ffi.VectorRFFIWrapper; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.ElementType; import com.oracle.truffle.r.runtime.gnur.SA_TYPE; import com.oracle.truffle.r.runtime.gnur.SEXPTYPE; import com.oracle.truffle.r.runtime.nmath.RMath; @@ -117,8 +118,6 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; import com.oracle.truffle.r.runtime.rng.RRNG; -import sun.misc.Unsafe; - /** * This class provides a simple Java-based implementation of {@link UpCallsRFFI}, where all the * argument values are standard Java types, i.e. no special types used by Truffle NFI or Truffle @@ -1464,7 +1463,7 @@ public int R_ReadConnection(int fd, long bufAddress, int size) { } catch (IOException e) { throw RError.error(RError.SHOW_CALLER, RError.Message.ERROR_READING_CONNECTION, e.getMessage()); } - UnsafeAdapter.UNSAFE.copyMemory(buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, bufAddress, Math.min(result, size)); + NativeMemory.copyMemory(buf, bufAddress, ElementType.BYTE, Math.min(result, size)); return result; } @@ -1473,7 +1472,7 @@ public int R_ReadConnection(int fd, long bufAddress, int size) { public int R_WriteConnection(int fd, long bufAddress, int size) { // Workaround using Unsafe until GR-5927 is fixed byte[] buf = new byte[size]; - UnsafeAdapter.UNSAFE.copyMemory(null, bufAddress, buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, size); + NativeMemory.copyMemory(bufAddress, buf, ElementType.BYTE, size); try (BaseRConnection fromIndex = RConnection.fromIndex(fd)) { final ByteBuffer wrapped = ByteBuffer.wrap(buf); fromIndex.writeBin(wrapped); diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java index 45fb0084a5..9e8c61af15 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/llvm/TruffleLLVM_DownCallNodeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, 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 @@ -110,8 +110,8 @@ protected void afterCall(Object before, NativeFunction fn, TruffleObject target, for (int i = 0; i < args.length; i++) { Object obj = args[i]; - if (obj instanceof NativeArray) { - ((NativeArray) obj).refresh(); + if (obj instanceof NativeArray) { + ((NativeArray) obj).refresh(); } } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java index 2f1f4ab941..c92eec65f9 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java @@ -27,7 +27,6 @@ import static com.oracle.truffle.r.runtime.ffi.RFFILog.logDownCallReturn; import static com.oracle.truffle.r.runtime.ffi.RFFILog.logEnabled; -import java.lang.reflect.Field; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; @@ -71,25 +70,6 @@ import com.oracle.truffle.r.runtime.ffi.ZipRFFI; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; -import sun.misc.Unsafe; - -class UnsafeAdapter { - public static final Unsafe UNSAFE = initUnsafe(); - - private static Unsafe initUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException se) { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(Unsafe.class); - } catch (Exception e) { - throw new RuntimeException("exception while trying to get Unsafe", e); - } - } - } -} public class TruffleNFI_Context extends RFFIContext { @@ -240,7 +220,7 @@ private long initCallbacksAddress() { private void initCallbacks(RContext context) { if (context.getKind() == ContextKind.SHARE_NOTHING) { // create and fill a new callbacks table - callbacks = NativeMemory.allocate(Callbacks.values().length * Unsafe.ARRAY_LONG_INDEX_SCALE, "callbacks"); + callbacks = NativeMemory.allocate(Callbacks.values().length * Long.BYTES, "callbacks"); InteropLibrary interop = InteropLibrary.getFactory().getUncached(); Object addCallback; try { @@ -283,9 +263,9 @@ private long pushCallbacks() { assert callbacks != 0L; if (singleThreadOnly && callbacksAddressThread == Thread.currentThread().getId()) { // Fast path for contexts used only from a single thread - long oldCallbacks = UnsafeAdapter.UNSAFE.getLong(callbacksAddress); + long oldCallbacks = NativeMemory.getLong(callbacksAddress); assert callbacksAddress != 0L; - UnsafeAdapter.UNSAFE.putLong(callbacksAddress, callbacks); + NativeMemory.putLong(callbacksAddress, callbacks); return oldCallbacks; } // Slow path: cache the address, but reinitialize it if the thread has changed, without @@ -299,10 +279,10 @@ private long pushCallbacks() { lastCallbacksAddress = initCallbacksAddress(); lastCallbacksAddressThread = Thread.currentThread().getId(); } - long oldCallbacks = UnsafeAdapter.UNSAFE.getLong(lastCallbacksAddress); + long oldCallbacks = NativeMemory.getLong(lastCallbacksAddress); assert lastCallbacksAddress != 0L; assert lastCallbacksAddressThread == Thread.currentThread().getId(); - UnsafeAdapter.UNSAFE.putLong(lastCallbacksAddress, callbacks); + NativeMemory.putLong(lastCallbacksAddress, callbacks); return oldCallbacks; } @@ -310,9 +290,9 @@ private void popCallbacks(long beforeValue) { if (beforeValue < 0) { return; } - assert !singleThreadOnly || UnsafeAdapter.UNSAFE.getLong(callbacksAddress) == callbacks : "invalid nesting of native calling contexts"; - assert singleThreadOnly || UnsafeAdapter.UNSAFE.getLong(lastCallbacksAddress) == callbacks : "invalid nesting of native calling contexts"; - UnsafeAdapter.UNSAFE.putLong(callbacksAddress, beforeValue); + assert !singleThreadOnly || NativeMemory.getLong(callbacksAddress) == callbacks : "invalid nesting of native calling contexts"; + assert singleThreadOnly || NativeMemory.getLong(lastCallbacksAddress) == callbacks : "invalid nesting of native calling contexts"; + NativeMemory.putLong(callbacksAddress, beforeValue); } protected void addLibRToDLLContextState(RContext context, DLLInfo libR) { diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java index 19a8387caf..f4a1d9e36e 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DownCallNodeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, 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 @@ -114,8 +114,8 @@ protected void afterCall(Object before, NativeFunction fn, TruffleObject target, (RContext.getInstance().getRFFI(TruffleNFI_Context.class)).afterDowncall(before, RFFIFactory.Type.NFI); for (Object obj : args) { // TODO: can this ever happen in NFI? - if (obj instanceof NativeArray) { - ((NativeArray) obj).refresh(); + if (obj instanceof NativeArray) { + ((NativeArray) obj).refresh(); } } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java index 15e524a007..4a1999745e 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, 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 @@ -22,15 +22,15 @@ */ package com.oracle.truffle.r.ffi.impl.nfi; -import java.nio.charset.StandardCharsets; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; -import com.oracle.truffle.r.runtime.ffi.interop.UnsafeAdapter; +import java.nio.charset.StandardCharsets; public class TruffleNFI_Utils { static String getString(long address, int len) { byte[] byteArray = new byte[len]; for (int i = 0; i < len; i++) { - byteArray[i] = UnsafeAdapter.UNSAFE.getByte(address + i); + byteArray[i] = NativeMemory.getByte(address, i); } return new String(byteArray, StandardCharsets.UTF_8); } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MathFunctionsNodes.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MathFunctionsNodes.java index 27c3b73b28..e7e965090c 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MathFunctionsNodes.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/MathFunctionsNodes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, 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 @@ -27,9 +27,7 @@ import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; @@ -40,7 +38,9 @@ import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector; import com.oracle.truffle.r.runtime.data.nodes.GetReadonlyData; -import com.oracle.truffle.r.runtime.ffi.interop.UnsafeAdapter; +import com.oracle.truffle.r.runtime.ffi.util.ReadDoublePointerNode; +import com.oracle.truffle.r.runtime.ffi.util.ReadIntPointerNode; +import com.oracle.truffle.r.runtime.ffi.util.WritePointerNode; import com.oracle.truffle.r.runtime.interop.ConvertForeignObjectNode; import com.oracle.truffle.r.runtime.nmath.BesselFunctions; import com.oracle.truffle.r.runtime.nmath.Beta; @@ -58,66 +58,17 @@ public final class MathFunctionsNodes { @GenerateUncached public abstract static class RfPnormBothNode extends FFIUpCallNode.Arg5 { - @Specialization(limit = "getInteropLibraryCacheSize()") + @Specialization protected Object evaluate(double x, Object cum, Object ccum, int lowerTail, int logP, - @CachedLibrary("cum") InteropLibrary cumInterop, - @CachedLibrary("ccum") InteropLibrary ccumInterop) { + @Cached ReadDoublePointerNode readCumNode, + @Cached WritePointerNode writeCumNode, + @Cached WritePointerNode writeCCumNode) { // cum is R/W double* with size==1 and ccum is Writeonly double* with size==1 - double[] cumArr = new double[1]; + double[] cumArr = new double[]{readCumNode.read(cum)}; double[] ccumArr = new double[1]; - long cumPtr; - long ccumPtr; - if (cumInterop.hasArrayElements(cum)) { - cumPtr = 0L; - try { - cumArr[0] = ((Number) cumInterop.readArrayElement(cum, 0)).doubleValue(); - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw RInternalError.shouldNotReachHere(e); - } - } else { - if (!cumInterop.isPointer(cum)) { - cumInterop.toNative(cum); - } - try { - cumPtr = cumInterop.asPointer(cum); - cumArr[0] = UnsafeAdapter.UNSAFE.getDouble(cumPtr); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - } - - if (cumInterop.hasArrayElements(cum)) { - ccumPtr = 0L; - } else { - try { - if (!ccumInterop.isPointer(ccum)) { - ccumInterop.toNative(ccum); - } - ccumPtr = ccumInterop.asPointer(ccum); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - } - PnormBoth.evaluate(x, cumArr, ccumArr, lowerTail != 0, logP != 0); - if (cumPtr != 0L) { - UnsafeAdapter.UNSAFE.putDouble(cumPtr, cumArr[0]); - } else { - try { - cumInterop.writeArrayElement(cum, 0, cumArr[0]); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("WRITE message support expected"); - } - } - if (ccumPtr != 0L) { - UnsafeAdapter.UNSAFE.putDouble(ccumPtr, ccumArr[0]); - } else { - try { - ccumInterop.writeArrayElement(ccum, 0, ccumArr[0]); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("WRITE message support expected"); - } - } + writeCumNode.write(cum, cumArr[0]); + writeCCumNode.write(ccum, ccumArr[0]); return RNull.instance; } @@ -253,40 +204,13 @@ public static LGammafnNode getUncached() { @GenerateUncached public abstract static class LGammafnSignNode extends FFIUpCallNode.Arg2 { - @Specialization(limit = "getInteropLibraryCacheSize()") + @Specialization protected Object evaluate(double a, Object sgn, - @CachedLibrary("sgn") InteropLibrary sgnInterop) { - int[] sgnArr = new int[1]; - long sgnPtr; - if (sgnInterop.hasArrayElements(sgn)) { - sgnPtr = 0L; - try { - sgnArr[0] = ((Number) sgnInterop.readArrayElement(sgn, 0)).intValue(); - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw RInternalError.shouldNotReachHere(e); - } - } else { - if (!sgnInterop.isPointer(sgn)) { - sgnInterop.toNative(sgn); - } - try { - sgnPtr = sgnInterop.asPointer(sgn); - sgnArr[0] = UnsafeAdapter.UNSAFE.getInt(sgnPtr); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - } - + @Cached WritePointerNode writePointerNode, + @Cached ReadIntPointerNode readIntPointerNode) { + int[] sgnArr = new int[]{readIntPointerNode.execute(sgn, 0)}; double result = GammaFunctions.lgammafnSign(a, sgnArr); - if (sgnPtr != 0L) { - UnsafeAdapter.UNSAFE.putInt(sgnPtr, sgnArr[0]); - } else { - try { - sgnInterop.writeArrayElement(sgn, 0, sgnArr[0]); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("WRITE message support expected"); - } - } + writePointerNode.execute(sgn, 0, sgnArr[0]); return result; } @@ -303,105 +227,23 @@ public static LGammafnSignNode getUncached() { @GenerateUncached public abstract static class DpsiFnNode extends FFIUpCallNode.Arg7 { - @Specialization(limit = "getInteropLibraryCacheSize()") + @Specialization protected Object evaluate(double x, int n, int kode, @SuppressWarnings("unused") int m, Object ans, Object nz, Object ierr, - @CachedLibrary("ans") InteropLibrary ansInterop, - @CachedLibrary("nz") InteropLibrary nzInterop, - @CachedLibrary("ierr") InteropLibrary ierrInterop) { + @Cached ReadDoublePointerNode readAns, + @Cached ReadIntPointerNode readNz, + @Cached ReadIntPointerNode readIErr, + @Cached WritePointerNode writeAns, + @Cached WritePointerNode writeNz, + @Cached WritePointerNode writeIErr) { // ans is R/W double* size==1 and nz is R/W int* size==1 // ierr is R/W int* size==1 - double ansIn; - int nzIn; - int ierrIn; - long ansPtr; - if (ansInterop.hasArrayElements(ans)) { - ansPtr = 0L; - try { - ansIn = ((Number) ansInterop.readArrayElement(ans, 0)).doubleValue(); - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw RInternalError.shouldNotReachHere(e); - } - } else { - if (!ansInterop.isPointer(ans)) { - ansInterop.toNative(ans); - } - try { - ansPtr = ansInterop.asPointer(ans); - ansIn = UnsafeAdapter.UNSAFE.getDouble(ansPtr); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - } - - long nzPtr; - if (nzInterop.hasArrayElements(nz)) { - nzPtr = 0L; - - try { - nzIn = ((Number) nzInterop.readArrayElement(nz, 0)).intValue(); - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw RInternalError.shouldNotReachHere(e); - } - } else { - if (!nzInterop.isPointer(nz)) { - nzInterop.toNative(nz); - } - try { - nzPtr = nzInterop.asPointer(nz); - nzIn = UnsafeAdapter.UNSAFE.getInt(nzPtr); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - } - - long ierrPtr; - if (ierrInterop.hasArrayElements(ierr)) { - ierrPtr = 0L; - try { - ierrIn = ((Number) ierrInterop.readArrayElement(ierr, 0)).intValue(); - } catch (UnsupportedMessageException | InvalidArrayIndexException e) { - throw RInternalError.shouldNotReachHere(e); - } - } else { - if (!ierrInterop.isPointer(ierr)) { - ierrInterop.toNative(ierr); - } - try { - ierrPtr = ierrInterop.asPointer(ierr); - ierrIn = UnsafeAdapter.UNSAFE.getInt(ierrPtr); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - } - + double ansIn = readAns.read(ans); + int nzIn = readNz.read(nz); + int ierrIn = readIErr.read(ierr); GammaFunctions.DpsiFnResult result = GammaFunctions.dpsifn(x, n, kode, ansIn, nzIn, ierrIn); - if (ansPtr != 0L) { - UnsafeAdapter.UNSAFE.putDouble(ansPtr, result.ans); - } else { - try { - ansInterop.writeArrayElement(ans, 0, result.ans); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("WRITE message support expected"); - } - } - if (nzPtr != 0L) { - UnsafeAdapter.UNSAFE.putInt(nzPtr, result.nz); - } else { - try { - nzInterop.writeArrayElement(nz, 0, result.nz); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("WRITE message support expected"); - } - } - if (ierrPtr != 0L) { - UnsafeAdapter.UNSAFE.putInt(ierrPtr, result.ierr); - } else { - try { - ierrInterop.writeArrayElement(ierr, 0, result.ierr); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("WRITE message support expected"); - } - } + writeAns.write(ans, result.ans); + writeNz.write(nz, result.nz); + writeIErr.write(ierr, result.ierr); return RNull.instance; } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java index 260b8cecd1..c151839005 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nodes/TryRfEvalNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, 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 @@ -22,18 +22,17 @@ */ package com.oracle.truffle.r.ffi.impl.nodes; -import com.oracle.truffle.api.interop.InteropException; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.r.runtime.DSLConfig; import com.oracle.truffle.r.runtime.RErrorHandling; -import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.data.RNull; -import com.oracle.truffle.r.runtime.ffi.interop.UnsafeAdapter; +import com.oracle.truffle.r.runtime.ffi.util.WritePointerNode; public final class TryRfEvalNode extends FFIUpCallNode.Arg4 { @Child private RfEvalNode rfEvalNode = RfEvalNode.create(); @Child private InteropLibrary interop = InteropLibrary.getFactory().createDispatched(DSLConfig.getInteropLibraryCacheSize()); + @Child private WritePointerNode writePointerNode; @Override public Object executeObject(Object expr, Object env, Object errorFlag, Object silent) { @@ -52,22 +51,16 @@ public Object executeObject(Object expr, Object env, Object errorFlag, Object si RErrorHandling.restoreStacks(handlerStack, restartStack); } if (!interop.isNull(errorFlag)) { - if (interop.isPointer(errorFlag)) { - long errorFlagPtr; - try { - errorFlagPtr = interop.asPointer(errorFlag); - } catch (UnsupportedMessageException e) { - throw RInternalError.shouldNotReachHere("IS_POINTER message returned true, AS_POINTER should not fail"); - } - UnsafeAdapter.UNSAFE.putInt(errorFlagPtr, ok ? 0 : 1); - } else { - try { - interop.writeArrayElement(errorFlag, 0, 1); - } catch (InteropException e) { - throw RInternalError.shouldNotReachHere("Rf_tryEval errorFlag should be either pointer or support WRITE message"); - } - } + writeErrorFlag(errorFlag, ok); } return result; } + + public void writeErrorFlag(Object errorFlag, boolean ok) { + if (writePointerNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + writePointerNode = insert(WritePointerNode.create()); + } + writePointerNode.execute(errorFlag, 0, ok ? 0 : 1); + } } 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 89651141a6..4941b9118e 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 @@ -27,7 +27,6 @@ import java.lang.management.ManagementFactory; import java.lang.ref.Reference; import java.lang.ref.WeakReference; -import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; @@ -53,10 +52,8 @@ import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; -import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RLogger; import com.oracle.truffle.r.runtime.RRuntime; @@ -70,40 +67,9 @@ import com.oracle.truffle.r.runtime.data.nodes.ShareObjectNode; import com.oracle.truffle.r.runtime.ffi.FFIMaterializeNode; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.ElementType; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.NativeMemoryWrapper; import com.oracle.truffle.r.runtime.ffi.util.ResourcesCleaner.ReleasableWeakReference; -import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; - -import sun.misc.Unsafe; - -abstract class InteropRootNode extends RootNode { - InteropRootNode() { - super(RContext.getInstance().getLanguage()); - } - - @Override - public final SourceSection getSourceSection() { - return RSyntaxNode.INTERNAL; - } -} - -class UnsafeAdapter { - public static final Unsafe UNSAFE = initUnsafe(); - - private static Unsafe initUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException se) { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(Unsafe.class); - } catch (Exception e) { - throw new RuntimeException("exception while trying to get Unsafe", e); - } - } - } -} /** * Provides API to work with objects returned by {@link RBaseObject#getNativeMirror()}. The native @@ -120,6 +86,11 @@ private NativeDataAccess() { // no instances } + /** + * Objects implementing this interface can provide custom pointer that will be used as the SEXP + * opaque pointer. This value is also used for the data-pointer and it will be cleaned + * automatically, i.e., the implementor is not responsible for freeing the memory. + */ public interface CustomNativeMirror { long getCustomMirrorAddress(); } @@ -216,8 +187,10 @@ public static final class NativeMirror implements TruffleObject { */ private NativeMemoryWrapper dataAddress; /** - * Length of the native data array. E.g. for CHARSXP this is not just the length of the Java - * String. + * Length of the native data array in terms of the items stored in the vector. There are two + * situations where this is not straightforward: for complex vectors this is the count of + * complex numbers, and for CHARSXP this is the length of the Java string + one for the + * terminating byte. */ private long length; @@ -331,14 +304,14 @@ public void setExternalDataAddress(long address) { } } - long setDataAddress(long address) { + NativeMemoryWrapper setDataAddress(long address) { // use setExternalDataAddress for empty data address assert address != getEmptyDataAddress(); this.dataAddress = NativeMemory.wrapNativeMemory(address, delegate); if (dataAddressToNativeMirrors != null) { addToAddressDebugMapping(address); } - return address; + return dataAddress; } @TruffleBoundary @@ -347,16 +320,15 @@ private void addToAddressDebugMapping(long address) { } @TruffleBoundary - void allocateNative(Object source, int len, int trueLen, int elementBase, int elementSize) { + void allocateNative(Object source, int vectorLength, long elementsCount, ElementType type) { assert getDataAddress() == 0; - if (len != 0) { - long bytesCount = trueLen * (long) elementSize; - setDataAddress(NativeMemory.allocate(bytesCount, source)); - UnsafeAdapter.UNSAFE.copyMemory(source, elementBase, null, dataAddress.getAddress(), bytesCount); + if (vectorLength != 0) { + setDataAddress(NativeMemory.allocate(type, elementsCount, source)); + NativeMemory.copyMemory(source, dataAddress, type, elementsCount); } else { setExternalDataAddress(getEmptyDataAddress()); } - this.length = len; + this.length = vectorLength; // ensure that marker address is not used assert this.length == 0 || dataAddress.getAddress() != getEmptyDataAddress(); @@ -365,11 +337,10 @@ void allocateNative(Object source, int len, int trueLen, int elementBase, int el @TruffleBoundary void allocateNativeString(byte[] bytes) { assert getDataAddress() == 0; - setDataAddress(NativeMemory.allocate(bytes.length + 1, "NativeString")); - UnsafeAdapter.UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, dataAddress.getAddress(), bytes.length); - UnsafeAdapter.UNSAFE.putByte(dataAddress.getAddress() + bytes.length, (byte) 0); // C - // strings - // terminator + setDataAddress(NativeMemory.allocate(bytes.length + 1L, "NativeString")); + NativeMemory.copyMemory(bytes, dataAddress, ElementType.BYTE, bytes.length); + // append C strings termination + NativeMemory.putByte(dataAddress, bytes.length, (byte) 0); this.length = bytes.length + 1; // ensure that marker address is not used @@ -381,9 +352,9 @@ void allocateNative(CharSXPWrapper[] wrappers) { if (wrappers.length == 0) { setExternalDataAddress(getEmptyDataAddress()); } else { - long addr = setDataAddress(NativeMemory.allocate(wrappers.length * (long) Long.BYTES, "CharSXPWrapper")); + NativeMemoryWrapper addr = setDataAddress(NativeMemory.allocate(wrappers.length * (long) Long.BYTES, "CharSXPWrapper")); for (int i = 0; i < wrappers.length; i++) { - UnsafeAdapter.UNSAFE.putLong(addr + (long) i * Long.BYTES, getPointer(wrappers[i])); + NativeMemory.putLong(addr, i, getPointer(wrappers[i])); } } } @@ -393,7 +364,7 @@ void allocateNative(Object[] elements) { if (elements.length == 0) { setExternalDataAddress(getEmptyDataAddress()); } else { - long addr = setDataAddress(NativeMemory.allocate(elements.length * (long) Long.BYTES, "SEXP array")); + NativeMemoryWrapper addr = setDataAddress(NativeMemory.allocate(elements.length * (long) Long.BYTES, "SEXP array")); for (int i = 0; i < elements.length; i++) { Object element = elements[i]; Object materialized = FFIMaterializeNode.uncachedMaterialize(element); @@ -401,7 +372,7 @@ void allocateNative(Object[] elements) { elements[i] = ShareObjectNode.executeUncached(materialized); } if (materialized instanceof RBaseObject) { - UnsafeAdapter.UNSAFE.putLong(addr + (long) i * Long.BYTES, getPointer((RBaseObject) materialized)); + NativeMemory.putLong(addr, i, getPointer((RBaseObject) materialized)); } else { throw RInternalError.shouldNotReachHere(materialized == null ? "null" : materialized.getClass().getSimpleName()); } @@ -540,179 +511,150 @@ private static void printDataAccessErrorLocation(long address) { // methods operating on the native mirror object directly: public static int getIntNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return UnsafeAdapter.UNSAFE.getInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE); + return NativeMemory.getInt(nativeMirror.dataAddress, index); } public static double getDoubleNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return UnsafeAdapter.UNSAFE.getDouble(address + (long) index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + return NativeMemory.getDouble(nativeMirror.dataAddress, index); } public static byte getLogicalNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return RRuntime.int2logical(UnsafeAdapter.UNSAFE.getInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE)); + return RRuntime.int2logical(NativeMemory.getInt(nativeMirror.dataAddress, index)); } public static byte getRawNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return UnsafeAdapter.UNSAFE.getByte(address + (long) index * Unsafe.ARRAY_BYTE_INDEX_SCALE); + return NativeMemory.getByte(nativeMirror.dataAddress, index); } public static RComplex getComplexNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return RComplex.valueOf(UnsafeAdapter.UNSAFE.getDouble(address + index * 2L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE), - UnsafeAdapter.UNSAFE.getDouble(address + (index * 2L + 1L) * Unsafe.ARRAY_DOUBLE_INDEX_SCALE)); + return RComplex.valueOf(NativeMemory.getDouble(nativeMirror.dataAddress, index * 2L), + NativeMemory.getDouble(nativeMirror.dataAddress, index * 2L + 1L)); } public static double getComplexNativeMirrorDataR(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return UnsafeAdapter.UNSAFE.getDouble(address + index * 2L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + return NativeMemory.getDouble(nativeMirror.dataAddress, +index * 2L); } public static double getComplexNativeMirrorDataI(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - return UnsafeAdapter.UNSAFE.getDouble(address + (index * 2L + 1L) * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + return NativeMemory.getDouble(nativeMirror.dataAddress, +index * 2L + 1L); } public static CharSXPWrapper getStringNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; - long elemAddr = UnsafeAdapter.UNSAFE.getLong(address + (long) index * Long.BYTES); + assert nativeMirror.getDataAddress() != 0; + assert index < nativeMirror.length; + long elemAddr = NativeMemory.getLong(nativeMirror.dataAddress, index); assert elemAddr != 0L; return (CharSXPWrapper) NativeDataAccess.lookup(elemAddr); } public static Object getListElementNativeMirrorData(NativeMirror nativeMirror, int index) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; - long elemAddr = UnsafeAdapter.UNSAFE.getLong(address + (long) index * Long.BYTES); + assert nativeMirror.getDataAddress() != 0; + assert index < nativeMirror.length; + long elemAddr = NativeMemory.getLong(nativeMirror.dataAddress, index); assert elemAddr != 0L; return NativeDataAccess.lookup(elemAddr); } public static void setNativeMirrorDoubleData(NativeMirror nativeMirror, int index, double value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - UnsafeAdapter.UNSAFE.putDouble(address + (long) index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, value); + NativeMemory.putDouble(nativeMirror.dataAddress, index, value); } public static void setNativeMirrorComplexRealPartData(NativeMirror nativeMirror, int index, double value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; - assert index < nativeMirror.length; - UnsafeAdapter.UNSAFE.putDouble(address + 2L * index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, value); + assert nativeMirror.getDataAddress() != 0; + assert 2L * index < nativeMirror.length; + NativeMemory.putDouble(nativeMirror.dataAddress, 2L * index, value); } public static void setNativeMirrorComplexImaginaryPartData(NativeMirror nativeMirror, int index, double value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; - assert index < nativeMirror.length; - UnsafeAdapter.UNSAFE.putDouble(address + (2L * index + 1L) * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, value); + assert nativeMirror.getDataAddress() != 0; + assert 2L * index + 1L < nativeMirror.length; + NativeMemory.putDouble(nativeMirror.dataAddress, 2L * index + 1L, value); } public static void setNativeMirrorRawData(NativeMirror nativeMirror, int index, byte value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - UnsafeAdapter.UNSAFE.putByte(address + (long) index * Unsafe.ARRAY_BYTE_INDEX_SCALE, value); + NativeMemory.putByte(nativeMirror.dataAddress, index, value); } public static void setNativeMirrorIntData(NativeMirror nativeMirror, int index, int value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - UnsafeAdapter.UNSAFE.putInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE, value); + NativeMemory.putInt(nativeMirror.dataAddress, index, value); } public static void setNativeMirrorLogicalData(NativeMirror nativeMirror, int index, byte logical) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; - assert index < nativeMirror.length; - UnsafeAdapter.UNSAFE.putInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE, RRuntime.logical2int(logical)); + setNativeMirrorIntData(nativeMirror, index, RRuntime.logical2int(logical)); } public static void setNativeMirrorStringData(NativeMirror nativeMirror, int index, CharSXPWrapper value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; - long asPointer = getPointer(value); - UnsafeAdapter.UNSAFE.putLong(address + (long) index * Long.BYTES, asPointer); + NativeMemory.putLong(nativeMirror.dataAddress, index, asPointer); } public static void setNativeMirrorListData(NativeMirror nativeMirror, int index, Object value) { - long address = nativeMirror.dataAddress.getAddress(); - assert address != 0; + assert nativeMirror.getDataAddress() != 0; assert index < nativeMirror.length; if (value instanceof RBaseObject) { long asPointer = getPointer((RBaseObject) value); - UnsafeAdapter.UNSAFE.putLong(address + (long) index * Long.BYTES, asPointer); + NativeMemory.putLong(nativeMirror.dataAddress, index, asPointer); } else { - throw RInternalError.shouldNotReachHere(); + throw RInternalError.shouldNotReachHere("value: " + value); } } - public static double[] copyDoubleNativeData(NativeMirror mirrorObj) { - NativeMirror mirror = mirrorObj; - long address = mirror.dataAddress.getAddress(); - assert address != 0; + public static double[] copyDoubleNativeData(NativeMirror mirror) { + assert mirror.getDataAddress() != 0; double[] data = new double[(int) mirror.length]; - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, (long) data.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + NativeMemory.copyMemory(mirror.dataAddress, data, ElementType.DOUBLE, data.length); return data; } - public static double[] copyComplexNativeData(NativeMirror mirrorObj) { - NativeMirror mirror = mirrorObj; - long address = mirror.dataAddress.getAddress(); - assert address != 0; + public static double[] copyComplexNativeData(NativeMirror mirror) { + assert mirror.getDataAddress() != 0; double[] data = new double[(int) (mirror.length << 1)]; - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, (long) data.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + NativeMemory.copyMemory(mirror.dataAddress, data, ElementType.DOUBLE, data.length); return data; } - public static int[] copyIntNativeData(NativeMirror mirrorObj) { - NativeMirror mirror = mirrorObj; - long address = mirror.dataAddress.getAddress(); - assert address != 0; + public static int[] copyIntNativeData(NativeMirror mirror) { + assert mirror.getDataAddress() != 0; int[] data = new int[(int) mirror.length]; - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_INT_BASE_OFFSET, (long) data.length * Unsafe.ARRAY_INT_INDEX_SCALE); + NativeMemory.copyMemory(mirror.dataAddress, data, ElementType.INT, data.length); return data; } - @TruffleBoundary - public static byte[] copyByteNativeData(NativeMirror mirrorObj) { - NativeMirror mirror = mirrorObj; - long address = mirror.dataAddress.getAddress(); - assert address != 0; + public static byte[] copyByteNativeData(NativeMirror mirror) { + assert mirror.getDataAddress() != 0; byte[] data = new byte[(int) mirror.length]; - UnsafeAdapter.UNSAFE.copyMemory(null, address, data, Unsafe.ARRAY_BYTE_BASE_OFFSET, (long) data.length * Unsafe.ARRAY_BYTE_INDEX_SCALE); + NativeMemory.copyMemory(mirror.dataAddress, data, ElementType.BYTE, data.length); return data; } - public static String[] copyStringNativeData(NativeMirror mirrorObj) { - NativeMirror mirror = mirrorObj; - long address = mirror.dataAddress.getAddress(); - assert address != 0; + public static String[] copyStringNativeData(NativeMirror mirror) { + assert mirror.getDataAddress() != 0; String[] data = new String[(int) mirror.length]; for (int i = 0; i < mirror.length; i++) { - long elemAddr = UnsafeAdapter.UNSAFE.getLong(address + (long) i * Long.BYTES); + long elemAddr = NativeMemory.getLong(mirror.dataAddress, i); assert elemAddr != 0L; Object elem = lookup(elemAddr); assert elem instanceof CharSXPWrapper; @@ -721,13 +663,11 @@ public static String[] copyStringNativeData(NativeMirror mirrorObj) { return data; } - public static Object[] copyListNativeData(NativeMirror mirrorObj) { - NativeMirror mirror = mirrorObj; - long address = mirror.dataAddress.getAddress(); - assert address != 0; + public static Object[] copyListNativeData(NativeMirror mirror) { + assert mirror.getDataAddress() != 0; Object[] data = new Object[(int) mirror.length]; for (int i = 0; i < mirror.length; i++) { - long elemAddr = UnsafeAdapter.UNSAFE.getLong(address + (long) i * Long.BYTES); + long elemAddr = NativeMemory.getLong(mirror.dataAddress, i); assert elemAddr != 0L; Object elem = lookup(elemAddr); data[i] = elem; @@ -791,9 +731,9 @@ static void setData(RIntVector vector, int[] data, int index, int value) { if (noIntNative.isValid() || data != null) { data[index] = value; } else { - long address = vector.getNativeMirror().dataAddress.getAddress(); - assert address != 0; - UnsafeAdapter.UNSAFE.putInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE, value); + NativeMirror mirror = vector.getNativeMirror(); + assert mirror.getDataAddress() != 0; + NativeMemory.putInt(mirror.dataAddress, index, value); } } @@ -801,9 +741,9 @@ static byte getData(RLogicalVector vector, byte[] data, int index) { if (noLogicalNative.isValid() || data != null) { return data[index]; } else { - long address = vector.getNativeMirror().dataAddress.getAddress(); - assert address != 0; - return RRuntime.int2logical(UnsafeAdapter.UNSAFE.getInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE)); + NativeMirror mirror = vector.getNativeMirror(); + assert mirror.getDataAddress() != 0; + return RRuntime.int2logical(NativeMemory.getInt(mirror.dataAddress, index)); } } @@ -819,9 +759,9 @@ static void setData(RLogicalVector vector, byte[] data, int index, byte value) { if (noLogicalNative.isValid() || data != null) { data[index] = value; } else { - long address = vector.getNativeMirror().dataAddress.getAddress(); - assert address != 0; - UnsafeAdapter.UNSAFE.putInt(address + (long) index * Unsafe.ARRAY_INT_INDEX_SCALE, RRuntime.logical2int(value)); + NativeMirror mirror = vector.getNativeMirror(); + assert mirror.getDataAddress() != 0; + NativeMemory.putInt(mirror.dataAddress, index, RRuntime.logical2int(value)); } } @@ -887,9 +827,9 @@ static void setData(RRawVector vector, byte[] data, int index, byte value) { if (noRawNative.isValid() || data != null) { data[index] = value; } else { - long address = vector.getNativeMirror().dataAddress.getAddress(); - assert address != 0; - UnsafeAdapter.UNSAFE.putInt(address + (long) index * Unsafe.ARRAY_BYTE_INDEX_SCALE, value); + NativeMirror mirror = vector.getNativeMirror(); + assert mirror.getDataAddress() != 0; + NativeMemory.putByte(mirror.dataAddress, index, value); } } @@ -1012,10 +952,10 @@ static void setData(RComplexVector vector, double[] data, int index, double re, data[index * 2] = re; data[index * 2 + 1] = im; } else { - long address = vector.getNativeMirror().dataAddress.getAddress(); - assert address != 0; - UnsafeAdapter.UNSAFE.putDouble(address + index * 2L * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, re); - UnsafeAdapter.UNSAFE.putDouble(address + (index * 2L + 1L) * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, im); + NativeMirror mirror = vector.getNativeMirror(); + assert mirror.getDataAddress() != 0; + NativeMemory.putDouble(mirror.dataAddress, index * 2L, re); + NativeMemory.putDouble(mirror.dataAddress, index * 2L + 1L, im); } } @@ -1023,9 +963,9 @@ static void setData(RComplexVector vector, double[] data, int index, double valu if (noComplexNative.isValid() || data != null) { data[index] = value; } else { - long address = vector.getNativeMirror().dataAddress.getAddress(); - assert address != 0; - UnsafeAdapter.UNSAFE.putDouble(address + (long) index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, value); + NativeMirror mirror = vector.getNativeMirror(); + assert mirror.getDataAddress() != 0; + NativeMemory.putDouble(mirror.dataAddress, index, value); } } @@ -1112,14 +1052,13 @@ static String getData(CharSXPWrapper charSXPWrapper, String data) { return data; } else { NativeMirror mirror = charSXPWrapper.getNativeMirror(); - long address = mirror.dataAddress.getAddress(); - assert address != 0; + assert mirror.getDataAddress() != 0; int length = 0; - while (length < mirror.length && UnsafeAdapter.UNSAFE.getByte(address + length) != 0) { + while (length < mirror.length && NativeMemory.getByte(mirror.dataAddress, length) != 0) { length++; } byte[] bytes = new byte[length]; - UnsafeAdapter.UNSAFE.copyMemory(null, address, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); + NativeMemory.copyMemory(mirror.dataAddress, bytes, ElementType.BYTE, length); return new String(bytes, StandardCharsets.UTF_8); } } @@ -1129,10 +1068,9 @@ static byte getDataAt(CharSXPWrapper charSXPWrapper, byte[] data, int index) { return data[index]; } else { NativeMirror mirror = charSXPWrapper.getNativeMirror(); - long address = mirror.dataAddress.getAddress(); - assert address != 0; + assert mirror.getDataAddress() != 0; assert index < mirror.length; - return UnsafeAdapter.UNSAFE.getByte(address + index); + return NativeMemory.getByte(mirror.dataAddress, index); } } @@ -1141,10 +1079,9 @@ static int getDataLength(CharSXPWrapper charSXPWrapper, byte[] data) { return data.length; } else { NativeMirror mirror = charSXPWrapper.getNativeMirror(); - long address = mirror.dataAddress.getAddress(); - assert address != 0; + assert mirror.getDataAddress() != 0; int length = 0; - while (length < mirror.length && UnsafeAdapter.UNSAFE.getByte(address + length) != 0) { + while (length < mirror.length && NativeMemory.getByte(mirror.dataAddress, length) != 0) { length++; } return length; @@ -1237,7 +1174,7 @@ static long allocateNativeContents(RLogicalVector vector, byte[] data, int lengt intArray[i] = RRuntime.logical2int(data[i]); } noLogicalNative.invalidate(); - mirror.allocateNative(intArray, length, data.length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE); + mirror.allocateNative(intArray, length, data.length, ElementType.INT); } return mirror.dataAddress.getAddress(); } @@ -1249,7 +1186,7 @@ static long allocateNativeContents(RIntVector vector, int[] data, int length) { if (mirror.dataAddress == null) { assert mirror.length == 0 && mirror.truelength == 0 : "mirror.length=" + mirror.length + ", mirror.truelength=" + mirror.truelength; noIntNative.invalidate(); - mirror.allocateNative(data, length, data.length, Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE); + mirror.allocateNative(data, length, data.length, ElementType.INT); } return mirror.dataAddress.getAddress(); } @@ -1261,7 +1198,7 @@ static long allocateNativeContents(RRawVector vector, byte[] data, int length) { if (mirror.dataAddress == null) { assert mirror.length == 0 && mirror.truelength == 0 : "mirror.length=" + mirror.length + ", mirror.truelength=" + mirror.truelength; noRawNative.invalidate(); - mirror.allocateNative(data, length, data.length, Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE); + mirror.allocateNative(data, length, data.length, ElementType.BYTE); } return mirror.dataAddress.getAddress(); } @@ -1273,7 +1210,7 @@ static long allocateNativeContents(RDoubleVector vector, double[] data, int leng if (mirror.dataAddress == null) { assert mirror.length == 0 && mirror.truelength == 0 : "mirror.length=" + mirror.length + ", mirror.truelength=" + mirror.truelength; noDoubleNative.invalidate(); - mirror.allocateNative(data, length, data.length, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + mirror.allocateNative(data, length, data.length, ElementType.DOUBLE); } return mirror.dataAddress.getAddress(); } @@ -1285,7 +1222,7 @@ static long allocateNativeContents(RComplexVector vector, double[] data, int len if (mirror.dataAddress == null) { assert mirror.length == 0 && mirror.truelength == 0 : "mirror.length=" + mirror.length + ", mirror.truelength=" + mirror.truelength; noComplexNative.invalidate(); - mirror.allocateNative(data, length, data.length, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE * 2); + mirror.allocateNative(data, length, data.length * 2L, ElementType.DOUBLE); } return mirror.dataAddress.getAddress(); } @@ -1344,14 +1281,14 @@ public static long allocateNativeStringArray(String[] data) { size += bytes[i].length + 1; } long dataAddress = NativeMemory.allocate(size, "StringArray"); - long ptr = dataAddress + length * Long.BYTES; // start of the actual character data + long nextStringPtr = dataAddress + length * Long.BYTES; + // start of the actual character data: for (int i = 0; i < length; i++) { - UnsafeAdapter.UNSAFE.putLong(dataAddress + i * 8L, ptr); - UnsafeAdapter.UNSAFE.copyMemory(bytes[i], Unsafe.ARRAY_BYTE_BASE_OFFSET, null, ptr, bytes[i].length); - ptr += bytes[i].length; - UnsafeAdapter.UNSAFE.putByte(ptr++, (byte) 0); + NativeMemory.putLong(dataAddress, i, nextStringPtr); + NativeMemory.copyMemory(bytes[i], nextStringPtr, ElementType.BYTE, bytes[i].length); + NativeMemory.putByte(nextStringPtr, bytes[i].length, (byte) 0); + nextStringPtr += bytes[i].length + 1; } - assert ptr == dataAddress + size : "should have filled everything"; return dataAddress; } @@ -1360,7 +1297,7 @@ public static String[] copyBackNativeStringArray(long address, int length) { assert address != 0; String[] data = new String[length]; for (int i = 0; i < length; i++) { - long ptr = UnsafeAdapter.UNSAFE.getLong(address + i * 8L); + long ptr = NativeMemory.getLong(address, i); data[i] = readNativeString(ptr); } return data; @@ -1369,10 +1306,10 @@ public static String[] copyBackNativeStringArray(long address, int length) { @TruffleBoundary public static String readNativeString(long addr) { int len; - for (len = 0; UnsafeAdapter.UNSAFE.getByte(addr + len) != 0; len++) { + for (len = 0; NativeMemory.getByte(addr, len) != 0; len++) { } byte[] bytes = new byte[len]; - UnsafeAdapter.UNSAFE.copyMemory(null, addr, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len); + NativeMemory.copyMemory(addr, bytes, ElementType.BYTE, len); return new String(bytes, StandardCharsets.US_ASCII); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java index 16361f12ce..937c229358 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RObjectSize.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, 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 @@ -43,7 +43,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractVector; import com.oracle.truffle.r.runtime.env.REnvironment; -import sun.misc.Unsafe; +import static com.oracle.truffle.r.runtime.ffi.util.NativeMemory.OBJECT_HEADER_SIZE; +import static com.oracle.truffle.r.runtime.ffi.util.NativeMemory.OBJECT_SIZE; /** * Support for the sizing of the objects that flow through the interpreter, i.e., mostly @@ -55,8 +56,6 @@ public class RObjectSize { public static final int BYTE_SIZE = 1; private static final int CHAR_SIZE = 2; - private static final int OBJECT_SIZE = Unsafe.ARRAY_OBJECT_INDEX_SCALE; - private static final int OBJECT_HEADER_SIZE = Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + OBJECT_SIZE * 2; /** * Returns an estimate of the size of the this object in bytes. This is a snapshot and the size diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java index c4d1d7cf6a..a2ec8589cb 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIUnwrapVectorNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2020, 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 @@ -26,7 +26,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.r.runtime.data.StringArrayWrapper; +import com.oracle.truffle.r.runtime.ffi.interop.StringArrayWrapper; public abstract class CRFFIUnwrapVectorNode extends Node { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java index ae80860a71..41d72a547d 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/CRFFIWrapVectorNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2020, 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,7 +33,7 @@ import com.oracle.truffle.r.runtime.data.RBaseObject; import com.oracle.truffle.r.runtime.data.model.RAbstractVector.RMaterializedVector; import com.oracle.truffle.r.runtime.data.RStringVector; -import com.oracle.truffle.r.runtime.data.StringArrayWrapper; +import com.oracle.truffle.r.runtime.ffi.interop.StringArrayWrapper; import com.oracle.truffle.r.runtime.data.model.RAbstractVector; public abstract class CRFFIWrapVectorNode extends Node { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java deleted file mode 100644 index dc29c8323d..0000000000 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/UnsafeAdapter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - * under the terms of the GNU General Public License version 3 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 3 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 - * 3 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.runtime.ffi; - -import java.lang.reflect.Field; - -import sun.misc.Unsafe; - -public class UnsafeAdapter { - public static final Unsafe UNSAFE = initUnsafe(); - - private static Unsafe initUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException se) { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(Unsafe.class); - } catch (Exception e) { - throw new RuntimeException("exception while trying to get Unsafe", e); - } - } - } -} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArray.java index 1a684c3c53..e088d2580b 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArray.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArray.java @@ -22,35 +22,152 @@ */ package com.oracle.truffle.r.runtime.ffi.interop; -import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; +import com.oracle.truffle.api.dsl.CachedContext; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.llvm.spi.NativeTypeLibrary; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.TruffleRLanguage; +import com.oracle.truffle.r.runtime.data.RTruffleObject; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.NativeMemoryWrapper; -public abstract class NativeArray extends NativeArrayExport { +/** + * Wraps a Java array to make it look like an interop and native array. These objects can be sent to + * LLVM/NFI and they will mimic corresponding native array type. To make any changes made to this + * object either via interop or via pointer visible in the Java array, call {@link #refresh()}. + * Note: if you do not call {@link #refresh()} the changes may or may not be visible in the Java + * array. + */ +@ExportLibrary(InteropLibrary.class) +@ExportLibrary(NativeTypeLibrary.class) +public abstract class NativeArray implements RTruffleObject { + private NativeMemoryWrapper nativeMirror; + + @SuppressWarnings("static-method") + @ExportMessage + public final boolean hasNativeType() { + return true; + } + + @SuppressWarnings("static-method") + @ExportMessage + public final Object getNativeType(@CachedContext(TruffleRLanguage.class) RContext ctx) { + return getSulongArrayType(ctx); + } + + @SuppressWarnings("static-method") + @ExportMessage + public final boolean isPointer() { + return nativeAddress() != null; + } + + @ExportMessage + public final long asPointer() throws UnsupportedMessageException { + if (nativeMirror != null) { + return nativeMirror.getAddress(); + } + throw UnsupportedMessageException.create(); + } - protected T array; - @SuppressWarnings("unused") private NativeMemoryWrapper nativeMirror; + @ExportMessage + protected static class ToNative { + @Specialization(guards = "receiver.hasNativeAddress()") + static void doNothing(@SuppressWarnings("unused") NativeArray receiver) { + } + + @Specialization(guards = "!receiver.hasNativeAddress()") + static void doToNative(NativeArray receiver) { + receiver.nativeMirror = receiver.allocateNative(); + } + } + + @SuppressWarnings("static-method") + @ExportMessage + public final boolean hasArrayElements() { + return true; + } + + @ExportMessage + public final long getArraySize() { + return getArrayLength(); + } + + @ExportMessage + public final boolean isArrayElementReadable(long index) { + return index >= 0 && index < getArraySize(); + } - protected NativeArray(T array) { - this.array = array; + @ExportMessage + public final boolean isArrayElementModifiable(long index) { + return index >= 0 && index < getArraySize(); } - @Override - protected final long nativeAddress() { - return (nativeMirror != null) ? nativeMirror.getAddress() : 0L; + @SuppressWarnings("static-method") + @ExportMessage + public final boolean isArrayElementInsertable(@SuppressWarnings("unused") long index) { + return false; } - @Override - protected final long convertToNative() { - if (nativeMirror == null) { - nativeMirror = NativeMemory.wrapNativeMemory(allocateNative(), this); + @ExportMessage + protected static class ReadArrayElement { + @Specialization(guards = "receiver.hasNativeAddress()") + static Object readFromNative(NativeArray receiver, long index) { + return receiver.readFromNative(receiver.nativeMirror, RRuntime.interopArrayIndexToInt(index, receiver)); } - return nativeMirror.getAddress(); + + @Specialization(guards = "!receiver.hasNativeAddress()") + static Object readFromArray(NativeArray receiver, long index) { + return receiver.readFromArray(RRuntime.interopArrayIndexToInt(index, receiver)); + } + } + + @ExportMessage + protected static class WriteArrayElement { + @Specialization(guards = "receiver.hasNativeAddress()") + static void writeToNative(NativeArray receiver, long index, Object value) { + receiver.writeToNative(receiver.nativeMirror, RRuntime.interopArrayIndexToInt(index, receiver), value); + } + + @Specialization(guards = "!receiver.hasNativeAddress()") + static void writeToArray(NativeArray receiver, long index, Object value) { + receiver.writeToArray(RRuntime.interopArrayIndexToInt(index, receiver), value); + } + } + + protected abstract int getArrayLength(); + + protected abstract Object getArray(); + + protected abstract Object readFromNative(NativeMemoryWrapper nativeAddress, int index); + + protected abstract Object readFromArray(int index); + + protected abstract void writeToNative(NativeMemoryWrapper nativeAddress, int index, Object value); + + protected abstract void writeToArray(int index, Object value); + + protected abstract NativeMemoryWrapper allocateNative(); + + protected abstract void copyBackFromNative(NativeMemoryWrapper nativeAddress); + + protected abstract Object getSulongArrayType(RContext ctx); + + protected final boolean hasNativeAddress() { + return nativeMirror != null; + } + + protected final NativeMemoryWrapper nativeAddress() { + return nativeMirror; } - public final T refresh() { + public final Object refresh() { if (nativeMirror != null) { - copyBackFromNative(nativeMirror.getAddress()); + copyBackFromNative(nativeMirror); } - return array; + return getArray(); } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArrayExport.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArrayExport.java deleted file mode 100644 index d0c198dc4d..0000000000 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeArrayExport.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2018, 2019, 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 3 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 3 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 - * 3 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.runtime.ffi.interop; - -import com.oracle.truffle.api.dsl.CachedContext; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.llvm.spi.NativeTypeLibrary; -import com.oracle.truffle.r.runtime.context.RContext; -import com.oracle.truffle.r.runtime.context.TruffleRLanguage; -import com.oracle.truffle.r.runtime.data.RTruffleObject; - -@ExportLibrary(NativeTypeLibrary.class) -@ExportLibrary(InteropLibrary.class) -abstract class NativeArrayExport implements RTruffleObject { - - @SuppressWarnings("static-method") - @ExportMessage - public boolean hasNativeType() { - return true; - } - - @SuppressWarnings("static-method") - @ExportMessage - public Object getNativeType(@CachedContext(TruffleRLanguage.class) RContext ctx) { - return getSulongArrayType(ctx); - } - - @SuppressWarnings("static-method") - @ExportMessage - public boolean isPointer() { - return nativeAddress() != 0; - } - - @ExportMessage - public long asPointer() throws UnsupportedMessageException { - long na = nativeAddress(); - if (na != 0L) { - return na; - } - throw UnsupportedMessageException.create(); - } - - @ExportMessage - public void toNative() { - convertToNative(); - } - - protected abstract long allocateNative(); - - protected abstract void copyBackFromNative(long nativeAddress); - - protected abstract Object getSulongArrayType(RContext ctx); - - protected abstract long nativeAddress(); - - protected abstract long convertToNative(); - -} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java index 54123fb1c9..ab5c61f787 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeCharArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, 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 @@ -29,12 +29,14 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.llvm.spi.NativeTypeLibrary; /** * A {@link TruffleObject} that represents an array of {@code unsigned char} values, that is * {@code NULL} terminated in the C domain. */ @ExportLibrary(InteropLibrary.class) +@ExportLibrary(NativeTypeLibrary.class) public final class NativeCharArray extends NativeUInt8Array { public NativeCharArray(byte[] bytes) { @@ -78,7 +80,7 @@ public String getStringFromOutputBuffer() { assert !fakesNullTermination() : "create the buffer string via createOutputBuffer()"; byte[] mbuf = getValue(); int i = 0; - while (mbuf[i] != 0 && i < mbuf.length) { + while (i < mbuf.length && mbuf[i] != 0) { i++; } return new String(mbuf, 0, i); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeDoubleArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeDoubleArray.java index 9216bf841c..735f3651c1 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeDoubleArray.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeDoubleArray.java @@ -22,117 +22,66 @@ */ package com.oracle.truffle.r.runtime.ffi.interop; -import static com.oracle.truffle.r.runtime.ffi.interop.UnsafeAdapter.UNSAFE; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.api.library.ExportMessage.Ignore; -import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.llvm.spi.NativeTypeLibrary; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; -import sun.misc.Unsafe; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.ElementType; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.NativeMemoryWrapper; @ExportLibrary(InteropLibrary.class) -public final class NativeDoubleArray extends NativeArray { +@ExportLibrary(NativeTypeLibrary.class) +public final class NativeDoubleArray extends NativeArray { + + private final double[] array; public NativeDoubleArray(double[] value) { - super(value); + this.array = value; } - @SuppressWarnings("static-method") - @ExportMessage - boolean hasArrayElements() { - return true; + @Override + protected Object getArray() { + return array; } - @ExportMessage - long getArraySize() { + @Override + protected int getArrayLength() { return array.length; } - @ExportMessage - boolean isArrayElementReadable(long index) { - return index >= 0 && index < getArraySize(); - } - - @ExportMessage - Object readArrayElement(long index) { - return read(RRuntime.interopArrayIndexToInt(index, this)); - } - - @ExportMessage - boolean isArrayElementModifiable(long index) { - return index >= 0 && index < getArraySize(); - } - - @SuppressWarnings("static-method") - @ExportMessage - boolean isArrayElementInsertable(@SuppressWarnings("unused") long index) { - return false; - } - - @Ignore - void writeArrayElement(long index, Object value) throws UnsupportedMessageException, UnsupportedTypeException, InvalidArrayIndexException { - InteropLibrary.getFactory().getUncached().writeArrayElement(this, index, value); + @Override + protected void writeToNative(NativeMemoryWrapper nativeAddress, int index, Object value) { + NativeMemory.putDouble(nativeAddress, index, (Double) value); } - @ExportMessage - static class WriteArrayElement { - - @Specialization - static void withoutClosure(NativeDoubleArray arr, long index, Long value) { - arr.write(RRuntime.interopArrayIndexToInt(index, arr), value); - } - - @Specialization - static void withClosure(NativeDoubleArray arr, long index, Integer value) { - arr.write(RRuntime.interopArrayIndexToInt(index, arr), value); - } - - @Specialization - static void withClosure(NativeDoubleArray arr, long index, Double value) { - arr.write(RRuntime.interopArrayIndexToInt(index, arr), value); - } + @Override + protected void writeToArray(int index, Object value) { + array[index] = (double) value; } - double read(int index) { - long nativeAddress = nativeAddress(); - if (nativeAddress != 0) { - return UNSAFE.getDouble(nativeAddress + index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); - } else { - return array[index]; - } + @Override + protected Object readFromNative(NativeMemoryWrapper nativeAddress, int index) { + return NativeMemory.getDouble(nativeAddress, index); } - void write(int index, double nv) { - long nativeAddress = nativeAddress(); - if (nativeAddress != 0) { - UNSAFE.putDouble(nativeAddress + index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, nv); - } else { - array[index] = nv; - } + @Override + protected Object readFromArray(int index) { + return array[index]; } @Override - @TruffleBoundary - protected long allocateNative() { - long nativeAddress = NativeMemory.allocate(array.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, "NativeDoubleArray"); - UNSAFE.copyMemory(array, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, null, nativeAddress, array.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + protected NativeMemoryWrapper allocateNative() { + long ptr = NativeMemory.allocate(array.length * Double.BYTES, "NativeDoubleArray"); + NativeMemoryWrapper nativeAddress = NativeMemory.wrapNativeMemory(ptr, this); + NativeMemory.copyMemory(array, nativeAddress, ElementType.DOUBLE, array.length); return nativeAddress; } @Override - @TruffleBoundary - protected void copyBackFromNative(long nativeAddress) { - // copy back - UNSAFE.copyMemory(null, nativeAddress, array, Unsafe.ARRAY_DOUBLE_BASE_OFFSET, array.length * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + protected void copyBackFromNative(NativeMemoryWrapper nativeAddress) { + NativeMemory.copyMemory(nativeAddress, array, ElementType.DOUBLE, array.length); } @Override diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeIntegerArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeIntegerArray.java index 01a86f9001..942acd8371 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeIntegerArray.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeIntegerArray.java @@ -22,117 +22,68 @@ */ package com.oracle.truffle.r.runtime.ffi.interop; -import static com.oracle.truffle.r.runtime.ffi.interop.UnsafeAdapter.UNSAFE; - import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.InvalidArrayIndexException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.interop.UnsupportedTypeException; import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.api.library.ExportMessage.Ignore; -import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.llvm.spi.NativeTypeLibrary; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; -import sun.misc.Unsafe; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.ElementType; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.NativeMemoryWrapper; @ExportLibrary(InteropLibrary.class) -public final class NativeIntegerArray extends NativeArray { +@ExportLibrary(NativeTypeLibrary.class) +public final class NativeIntegerArray extends NativeArray { + + private final int[] array; public NativeIntegerArray(int[] value) { - super(value); + this.array = value; } - @SuppressWarnings("static-method") - @ExportMessage - boolean hasArrayElements() { - return true; + @Override + protected Object getArray() { + return array; } - @ExportMessage - long getArraySize() { + @Override + protected int getArrayLength() { return array.length; } - @ExportMessage - boolean isArrayElementReadable(long index) { - return index >= 0 && index < getArraySize(); - } - - @ExportMessage - Object readArrayElement(long index) { - return read(RRuntime.interopArrayIndexToInt(index, this)); - } - - @ExportMessage - boolean isArrayElementModifiable(long index) { - return index >= 0 && index < getArraySize(); - } - - @SuppressWarnings("static-method") - @ExportMessage - boolean isArrayElementInsertable(@SuppressWarnings("unused") long index) { - return false; - } - - @Ignore - void writeArrayElement(long index, Object value) throws UnsupportedMessageException, UnsupportedTypeException, InvalidArrayIndexException { - InteropLibrary.getFactory().getUncached().writeArrayElement(this, index, value); + @Override + protected void writeToNative(NativeMemoryWrapper nativeAddress, int index, Object value) { + NativeMemory.putInt(nativeAddress, index, (Integer) value); } - @ExportMessage - static class WriteArrayElement { - - @Specialization - static void withoutClosure(NativeIntegerArray arr, long index, Long value) { - arr.write(RRuntime.interopArrayIndexToInt(index, arr), value.intValue()); - } - - @Specialization - static void withClosure(NativeIntegerArray arr, long index, Integer value) { - arr.write(RRuntime.interopArrayIndexToInt(index, arr), value); - } - - @Specialization - static void withClosure(NativeIntegerArray arr, long index, Double value) { - arr.write(RRuntime.interopArrayIndexToInt(index, arr), value.intValue()); - } + @Override + protected void writeToArray(int index, Object value) { + array[index] = (int) value; } - int read(int index) { - long nativeAddress = nativeAddress(); - if (nativeAddress != 0) { - return UNSAFE.getInt(nativeAddress + index * Unsafe.ARRAY_INT_INDEX_SCALE); - } else { - return array[index]; - } + @Override + protected Object readFromNative(NativeMemoryWrapper nativeAddress, int index) { + return NativeMemory.getInt(nativeAddress, index); } - void write(int index, int nv) { - long nativeAddress = nativeAddress(); - if (nativeAddress != 0) { - UNSAFE.putInt(nativeAddress + index * Unsafe.ARRAY_INT_INDEX_SCALE, nv); - } else { - array[index] = nv; - } + @Override + protected Object readFromArray(int index) { + return array[index]; } @Override @TruffleBoundary - protected long allocateNative() { - long nativeAddress = NativeMemory.allocate(array.length * Unsafe.ARRAY_INT_INDEX_SCALE, "NativeIntegerArray"); - UNSAFE.copyMemory(array, Unsafe.ARRAY_INT_BASE_OFFSET, null, nativeAddress, array.length * Unsafe.ARRAY_INT_INDEX_SCALE); + protected NativeMemoryWrapper allocateNative() { + long ptr = NativeMemory.allocate(array.length * Integer.BYTES, "NativeIntegerArray"); + NativeMemoryWrapper nativeAddress = NativeMemory.wrapNativeMemory(ptr, this); + NativeMemory.copyMemory(array, nativeAddress, ElementType.INT, array.length); return nativeAddress; } @Override - @TruffleBoundary - protected void copyBackFromNative(long nativeAddress) { - // copy back - UNSAFE.copyMemory(null, nativeAddress, array, Unsafe.ARRAY_INT_BASE_OFFSET, array.length * Unsafe.ARRAY_INT_INDEX_SCALE); + protected void copyBackFromNative(NativeMemoryWrapper nativeAddress) { + NativeMemory.copyMemory(nativeAddress, array, ElementType.INT, array.length); } @Override diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java index 9c6bdfebcb..27c1100234 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativePointer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, 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,46 +23,36 @@ package com.oracle.truffle.r.runtime.ffi.interop; import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RTruffleObject; /** * Created when a {@link RTruffleObject} subclass has no meaningful native representation, - * nevertheless a {@code Message#TO_NATIVE} message is sent to it. + * nevertheless a {@code toNative} message is sent to it. */ @ExportLibrary(InteropLibrary.class) -public class NativePointer implements RTruffleObject { +public final class NativePointer implements RTruffleObject { - /** - * This is used when an {@link RNull} is stored in memory (LLVM). - */ public static final NativePointer NULL_NATIVEPOINTER = new NativePointer(); - protected NativePointer() { - } - - public static boolean isInstance(TruffleObject obj) { - return obj instanceof NativePointer; + private NativePointer() { } @SuppressWarnings("static-method") @ExportMessage - final boolean isPointer() { + boolean isPointer() { return true; } @SuppressWarnings("static-method") @ExportMessage - final long asPointer() { + long asPointer() { return 0; } @ExportMessage - final void toNative() { - + void toNative() { + // nop } - } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java index 0e1132bf41..b9246f3713 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeRawArray.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2020, 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 @@ -22,6 +22,12 @@ */ package com.oracle.truffle.r.runtime.ffi.interop; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.llvm.spi.NativeTypeLibrary; + +@ExportLibrary(InteropLibrary.class) +@ExportLibrary(NativeTypeLibrary.class) public final class NativeRawArray extends NativeUInt8Array { public NativeRawArray(byte[] bytes) { super(bytes, false); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java index f79fc2cf1c..6f447b2410 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/NativeUInt8Array.java @@ -22,136 +22,89 @@ */ package com.oracle.truffle.r.runtime.ffi.interop; -import static com.oracle.truffle.r.runtime.ffi.UnsafeAdapter.UNSAFE; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.llvm.spi.NativeTypeLibrary; -import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; -import sun.misc.Unsafe; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.ElementType; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.NativeMemoryWrapper; /** * Parent class of {@link NativeRawArray} and {@link NativeCharArray}, that holds the common logic - * for a C type {@code uint8*}, that may or may not be {@code NULL} terminated (in the C domain), - * and may escape into the native domain via an UNBOX message. - * - * N.B. Java never stores a {@code NULL} value in a String or the byte array from - * {@link String#getBytes}. + * for a C type {@code uint8*}, that may or may not be {@code NULL} terminated (in the C domain). * - * If {@link #fakesNullTermination()} is {@code true}, then {@link #read} returns 0, else it is an - * error; similar for {@link #write}. + * The null termination is faked for Java arrays. If this object escapes to native code and we + * allocate native memory for it, then the native memory will be null terminated (and one byte + * longer). */ @ExportLibrary(InteropLibrary.class) @ExportLibrary(NativeTypeLibrary.class) -public abstract class NativeUInt8Array extends NativeArray { +public abstract class NativeUInt8Array extends NativeArray { + private byte[] array; private int effectiveLength; protected NativeUInt8Array(byte[] array, boolean nullTerminate) { - super(array); + this.array = array; this.effectiveLength = array.length + (nullTerminate ? 1 : 0); } - @SuppressWarnings("static-method") - @ExportMessage - boolean hasArrayElements() { - return true; - } - - @ExportMessage - long getArraySize() { - return array.length; - } - - @ExportMessage - boolean isArrayElementReadable(long index) { - return index >= 0 && index < getArraySize(); - } - - @ExportMessage - Object readArrayElement(long index) { - return read(RRuntime.interopArrayIndexToInt(index, this)); - } - - @ExportMessage - boolean isArrayElementModifiable(long index) { - return index >= 0 && index < getArraySize(); + public boolean fakesNullTermination() { + return array.length != effectiveLength; } - @SuppressWarnings("static-method") - @ExportMessage - boolean isArrayElementInsertable(@SuppressWarnings("unused") long index) { - return false; + @Override + protected Object getArray() { + return array; } - @ExportMessage - void writeArrayElement(long index, Object value) { - write(RRuntime.interopArrayIndexToInt(index, this), (byte) value); + @Override + protected int getArrayLength() { + return effectiveLength; } - public boolean fakesNullTermination() { - return array.length != effectiveLength; + @Override + protected void writeToNative(NativeMemoryWrapper nativeAddress, int index, Object value) { + NativeMemory.putByte(nativeAddress, index, (Byte) value); } - private void checkNativeIndex(int index) { - if (index < 0 || index >= effectiveLength) { - throw new ArrayIndexOutOfBoundsException(index); + @Override + protected void writeToArray(int index, Object value) { + if (!(index == array.length && fakesNullTermination())) { + array[index] = (byte) value; } + // otherwise ignore overwrite of the terminating zero, maybe warn? } - protected void writeObject(int index, Object value) { - write(index, (byte) value); - } - - void write(int index, byte value) { - long nativeAddress = nativeAddress(); - if (nativeAddress != 0) { - checkNativeIndex(index); - UNSAFE.putByte(nativeAddress + index, value); - } else { - if (index == array.length && fakesNullTermination()) { - // ignore - } else { - array[index] = value; - } - } + @Override + protected Object readFromNative(NativeMemoryWrapper nativeAddress, int index) { + return NativeMemory.getByte(nativeAddress, index); } - byte read(int index) { - long nativeAddress = nativeAddress(); - if (nativeAddress != 0) { - checkNativeIndex(index); - return UNSAFE.getByte(nativeAddress + index); - } else { - if (index == array.length && fakesNullTermination()) { - return (byte) 0; - } - return array[index]; + @Override + protected Object readFromArray(int index) { + if (index == array.length && fakesNullTermination()) { + return (byte) 0; } + return array[index]; } - int getSize() { - return array.length; - } - - @TruffleBoundary @Override - protected final long allocateNative() { - long nativeAddress = NativeMemory.allocate(effectiveLength, "NativeUInt8Array"); - UNSAFE.copyMemory(array, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, nativeAddress, array.length); + protected final NativeMemoryWrapper allocateNative() { + long ptr = NativeMemory.allocate(effectiveLength, "NativeUInt8Array"); + NativeMemoryWrapper nativeAddress = NativeMemory.wrapNativeMemory(ptr, this); + NativeMemory.copyMemory(array, nativeAddress, ElementType.BYTE, array.length); if (fakesNullTermination()) { - UNSAFE.putByte(nativeAddress + array.length, (byte) 0); + NativeMemory.putByte(nativeAddress, array.length, (byte) 0); } return nativeAddress; } public byte[] getValue() { - return refresh(); + refresh(); + return array; } public void setValue(byte[] newBytes, boolean isNullTerminated) { @@ -159,16 +112,13 @@ public void setValue(byte[] newBytes, boolean isNullTerminated) { effectiveLength = isNullTerminated ? array.length + 1 : array.length; } - @TruffleBoundary @Override - protected void copyBackFromNative(long nativeAddress) { - // copy back - UNSAFE.copyMemory(null, nativeAddress, array, Unsafe.ARRAY_BYTE_BASE_OFFSET, array.length); + protected void copyBackFromNative(NativeMemoryWrapper nativeAddress) { + NativeMemory.copyMemory(nativeAddress, array, ElementType.BYTE, array.length); } @Override protected Object getSulongArrayType(RContext ctx) { return ctx.getRFFI().getSulongArrayType((byte) 42); } - } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/StringArrayWrapper.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/StringArrayWrapper.java similarity index 81% rename from com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/StringArrayWrapper.java rename to com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/StringArrayWrapper.java index b60b90a0c3..6a1708f6e1 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/StringArrayWrapper.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/StringArrayWrapper.java @@ -20,17 +20,24 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.truffle.r.runtime.data; +package com.oracle.truffle.r.runtime.ffi.interop; +import com.oracle.truffle.api.dsl.CachedContext; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray; +import com.oracle.truffle.llvm.spi.NativeTypeLibrary; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.TruffleRLanguage; +import com.oracle.truffle.r.runtime.data.NativeDataAccess; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; import com.oracle.truffle.r.runtime.ffi.util.NativeMemory.NativeMemoryWrapper; @ExportLibrary(InteropLibrary.class) +@ExportLibrary(NativeTypeLibrary.class) public final class StringArrayWrapper implements TruffleObject { private NativeMemoryWrapper nativeMemory; @@ -41,6 +48,18 @@ public StringArrayWrapper(RStringVector vector) { this.vector = vector; } + @SuppressWarnings("static-method") + @ExportMessage + public boolean hasNativeType() { + return true; + } + + @SuppressWarnings("static-method") + @ExportMessage + public Object getNativeType(@CachedContext(TruffleRLanguage.class) RContext ctx) { + return ctx.getRFFI().getSulongArrayType(42L); + } + @SuppressWarnings("static-method") @ExportMessage boolean hasArrayElements() { @@ -97,7 +116,7 @@ public RStringVector copyBackFromNative() { contents[i] = nativeCharArray.getString(); } } - RStringVector copy = new RStringVector(contents, false); + RStringVector copy = RDataFactory.createStringVector(contents, false); copy.copyAttributesFrom(vector); return copy; } else { @@ -106,7 +125,7 @@ public RStringVector copyBackFromNative() { } else { try { String[] contents = NativeDataAccess.copyBackNativeStringArray(nativeMemory.getAddress(), vector.getLength()); - RStringVector copy = new RStringVector(contents, false); + RStringVector copy = RDataFactory.createStringVector(contents, false); copy.copyAttributesFrom(vector); return copy; } finally { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/UnsafeAdapter.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/UnsafeAdapter.java deleted file mode 100644 index 42884c5ca8..0000000000 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/interop/UnsafeAdapter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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 - * under the terms of the GNU General Public License version 3 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 3 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 - * 3 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.runtime.ffi.interop; - -import java.lang.reflect.Field; - -import sun.misc.Unsafe; - -public class UnsafeAdapter { - public static final Unsafe UNSAFE = initUnsafe(); - - private static Unsafe initUnsafe() { - try { - return Unsafe.getUnsafe(); - } catch (SecurityException se) { - try { - Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - return (Unsafe) theUnsafe.get(Unsafe.class); - } catch (Exception e) { - throw new RuntimeException("exception while trying to get Unsafe", e); - } - } - } -} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/NativeMemory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/NativeMemory.java index 080e95458d..bd4bfb8686 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/NativeMemory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/NativeMemory.java @@ -29,9 +29,11 @@ import java.lang.reflect.Field; import java.util.Arrays; import java.util.Map.Entry; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.SuppressFBWarnings; @@ -40,18 +42,40 @@ import sun.misc.Unsafe; /** - * Thin layer abstraction over {@code sun.misc.Unsafe}. - * - * TODO: replace all usages of Unsafe with this (GR-20618) + * Thin layer abstraction over {@code sun.misc.Unsafe}. Raw native pointers can be wrapped using + * {@link #wrapNativeMemory(long, Object)} to ensure the clean-up of the memory once the owning + * object is collected. */ public abstract class NativeMemory { + private NativeMemory() { // only static members } - public static final Unsafe UNSAFE = initUnsafe(); + public static final int OBJECT_SIZE = Unsafe.ARRAY_OBJECT_INDEX_SCALE; + public static final int OBJECT_HEADER_SIZE = Unsafe.ARRAY_BOOLEAN_BASE_OFFSET + OBJECT_SIZE * 2; + + public enum ElementType { + BYTE(1, Unsafe.ARRAY_BYTE_BASE_OFFSET), + INT(Integer.BYTES, Unsafe.ARRAY_INT_BASE_OFFSET), + LONG(Long.BYTES, Unsafe.ARRAY_LONG_BASE_OFFSET), + DOUBLE(Double.BYTES, Unsafe.ARRAY_DOUBLE_BASE_OFFSET); + + private final int bytes; + private final int offset; + + ElementType(int bytes, int offset) { + this.bytes = bytes; + this.offset = offset; + } + } + + private static final Unsafe UNSAFE = initUnsafe(); private static Unsafe initUnsafe() { + assert Integer.BYTES == Unsafe.ARRAY_INT_INDEX_SCALE; + assert Double.BYTES == Unsafe.ARRAY_DOUBLE_INDEX_SCALE; + assert Long.BYTES == Unsafe.ARRAY_LONG_INDEX_SCALE; try { return Unsafe.getUnsafe(); } catch (SecurityException se) { @@ -72,6 +96,13 @@ public static long allocate(long size, Object debugInfo) { return result; } + public static long allocate(ElementType type, long size, Object debugInfo) { + traceAllocateStart(size, debugInfo); + long result = UNSAFE.allocateMemory(size * type.bytes); + traceAllocate(result, size, debugInfo); + return result; + } + public static void free(long address, Object debugInfo) { traceFree(address, debugInfo); UNSAFE.freeMemory(address); @@ -82,10 +113,134 @@ public static NativeMemoryWrapper wrapNativeMemory(long address, Object owner) { return new FreeingNativeMemoryWrapper(address, owner); } + /** + * Allows to wrap native memory address in the same object as + * {@link #wrapNativeMemory(long, Object)}, but without cleaning-up the memory when the owning + * object is collected. This can be useful in places where {@link NativeMemoryWrapper} is + * expected but you do not have control over the life cycle of the given memory (i.e., it is + * externally owned). + */ public static NativeMemoryWrapper wrapExternalNativeMemory(long address, Object owner) { return new ExternalNativeMemoryWrapper(address, owner); } + public static void copyMemory(Object source, NativeMemoryWrapper destination, ElementType type, long elementsCount) { + copyMemory(source, type.offset, type.bytes, destination.getAddress(), elementsCount); + } + + public static void copyMemory(Object source, long destination, ElementType type, long elementsCount) { + copyMemory(source, type.offset, type.bytes, destination, elementsCount); + } + + private static void copyMemory(Object source, int elementBase, long elementSize, long destination, long elementsCount) { + // this takes relevant args as longs to make sure any calculations do not overflow + UNSAFE.copyMemory(source, elementBase, null, destination, elementSize * elementsCount); + } + + public static void copyMemory(NativeMemoryWrapper source, Object destination, ElementType type, int elementsCount) { + copyMemory(source.getAddress(), type.offset, type.bytes, destination, elementsCount); + } + + public static void copyMemory(long source, Object destination, ElementType type, int elementsCount) { + copyMemory(source, type.offset, type.bytes, destination, elementsCount); + } + + private static void copyMemory(long source, int elementBase, int elementSize, Object destination, int elementsCount) { + // this takes relevant args as longs to make sure any calculations do not overflow + UNSAFE.copyMemory(null, source, destination, elementBase, (long) elementSize * (long) elementsCount); + } + + public static void putByte(NativeMemoryWrapper address, long offset, byte value) { + putByte(address.getAddress(), offset, value); + } + + public static void putByte(long address, long offset, byte value) { + UNSAFE.putByte(address + offset * Unsafe.ARRAY_BYTE_INDEX_SCALE, value); + } + + public static void putInt(NativeMemoryWrapper address, long offset, int value) { + putInt(address.getAddress(), offset, value); + } + + public static void putInt(long address, long offset, int value) { + UNSAFE.putInt(address + offset * Unsafe.ARRAY_INT_INDEX_SCALE, value); + } + + public static void putLong(NativeMemoryWrapper address, long offset, long value) { + putLong(address.getAddress(), offset, value); + } + + public static void putLong(long address, long offset, long value) { + UNSAFE.putLong(address + offset * Unsafe.ARRAY_LONG_INDEX_SCALE, value); + } + + public static void putLong(long address, long value) { + putLong(address, 0, value); + } + + public static void putDouble(NativeMemoryWrapper address, long offset, double value) { + putDouble(address.getAddress(), offset, value); + } + + public static void putDouble(long address, long offset, double value) { + UNSAFE.putDouble(address + offset * Unsafe.ARRAY_DOUBLE_INDEX_SCALE, value); + } + + public static byte getByte(NativeMemoryWrapper dataAddress, long index) { + return getByte(dataAddress.getAddress(), index); + } + + public static byte getByte(long dataAddress, long index) { + return UNSAFE.getByte(dataAddress + index * Unsafe.ARRAY_BYTE_INDEX_SCALE); + } + + public static int getInt(NativeMemoryWrapper dataAddress, long index) { + return getInt(dataAddress.getAddress(), index); + } + + public static int getInt(long dataAddress, long index) { + return UNSAFE.getInt(dataAddress + index * Unsafe.ARRAY_INT_INDEX_SCALE); + } + + public static long getLong(NativeMemoryWrapper dataAddress, long index) { + return getLong(dataAddress.getAddress(), index); + } + + public static long getLong(long dataAddress, long index) { + return UNSAFE.getLong(dataAddress + index * Unsafe.ARRAY_LONG_INDEX_SCALE); + } + + public static long getLong(long dataAddress) { + return getLong(dataAddress, 0); + } + + public static double getDouble(NativeMemoryWrapper dataAddress, long index) { + return getDouble(dataAddress.getAddress(), +index); + } + + public static double getDouble(long dataAddress, long index) { + return UNSAFE.getDouble(dataAddress + index * Unsafe.ARRAY_DOUBLE_INDEX_SCALE); + } + + /** + * Puts an integer, double, byte or long at given address with given offset. The offset is + * interpreted {@code index * size of the value type in bytes}. + */ + public static void putValue(long address, long index, Object value) { + if (value instanceof Integer) { + NativeMemory.putInt(address, index, (Integer) value); + } else if (value instanceof Double) { + NativeMemory.putDouble(address, index, (Double) value); + } else if (value instanceof Byte) { + NativeMemory.putByte(address, index, (Byte) value); + } else if (value instanceof Long) { + NativeMemory.putLong(address, index, (Long) value); + } else { + CompilerDirectives.transferToInterpreter(); + throw RInternalError.shouldNotReachHere(Objects.toString(value)); + } + } + /** * Wraps given native memory address associated with given Java object. */ diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadDoublePointerNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadDoublePointerNode.java new file mode 100644 index 0000000000..46cd772aa5 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadDoublePointerNode.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020, 2020, 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 3 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 3 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 + * 3 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.runtime.ffi.util; + +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.runtime.DSLConfig; +import com.oracle.truffle.r.runtime.RInternalError; + +/** + * Encapsulates the operation of reading a double from a source object, which should represent a + * double pointer. This node tries to read the data using array messages of the interop protocol and + * if that fails it uses the {@code toNative} and {@code asPointer} messages to get the raw pointer + * and reads from it using unsafe access. + */ +@GenerateUncached +@ImportStatic(DSLConfig.class) +public abstract class ReadDoublePointerNode extends Node { + public final double read(Object source) { + return execute(source, 0); + } + + public abstract double execute(Object source, int index); + + @Specialization(guards = "targetLib.isArrayElementReadable(target, index)", limit = "getInteropLibraryCacheSize()") + protected static double getFromArray(Object target, int index, + @Cached AsDoubleNode asDoubleNode, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + return asDoubleNode.execute(targetLib.readArrayElement(target, index)); + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + throw RInternalError.shouldNotReachHere(); + } + } + + @Specialization(guards = {"!targetLib.isArrayElementWritable(target, index)"}, limit = "getInteropLibraryCacheSize()") + protected static double getFromNative(Object target, int index, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + targetLib.toNative(target); + long ptr = targetLib.asPointer(target); + return NativeMemory.getDouble(ptr, index); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere(); + } + } + + @GenerateUncached + @ImportStatic(DSLConfig.class) + public abstract static class AsDoubleNode extends Node { + public abstract double execute(Object value); + + @Specialization + static double doDouble(double i) { + return i; + } + + @Specialization(replaces = "doDouble", limit = "getInteropLibraryCacheSize()") + static double doOthers(Object value, + @CachedLibrary("value") InteropLibrary lib) { + try { + return lib.asDouble(value); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere(); + } + } + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadIntPointerNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadIntPointerNode.java new file mode 100644 index 0000000000..5a43cad2dd --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/ReadIntPointerNode.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020, 2020, 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 3 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 3 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 + * 3 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.runtime.ffi.util; + +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.runtime.DSLConfig; +import com.oracle.truffle.r.runtime.RInternalError; + +/** + * Encapsulates the operation of reading an integer from a source object, which should represent an + * integer pointer. This node tries to read the data using array messages of the interop protocol + * and if that fails it uses the {@code toNative} and {@code asPointer} messages to get the raw + * pointer and reads from it using unsafe access. + */ +@GenerateUncached +@ImportStatic(DSLConfig.class) +public abstract class ReadIntPointerNode extends Node { + public final int read(Object source) { + return execute(source, 0); + } + + public abstract int execute(Object source, int index); + + @Specialization(guards = "targetLib.isArrayElementReadable(target, index)", limit = "getInteropLibraryCacheSize()") + protected static int getFromArray(Object target, int index, + @Cached AsIntNode asIntNode, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + return asIntNode.execute(targetLib.readArrayElement(target, index)); + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + throw RInternalError.shouldNotReachHere(); + } + } + + @Specialization(guards = {"!targetLib.isArrayElementWritable(target, index)"}, limit = "getInteropLibraryCacheSize()") + protected static int getFromNative(Object target, int index, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + targetLib.toNative(target); + long ptr = targetLib.asPointer(target); + return NativeMemory.getInt(ptr, index); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere(); + } + } + + @GenerateUncached + @ImportStatic(DSLConfig.class) + public abstract static class AsIntNode extends Node { + public abstract int execute(Object value); + + @Specialization + static int doInt(int i) { + return i; + } + + @Specialization(replaces = "doInt", limit = "getInteropLibraryCacheSize()") + static int doOthers(Object value, + @CachedLibrary("value") InteropLibrary lib) { + try { + return lib.asInt(value); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere(); + } + } + } +} diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/WritePointerNode.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/WritePointerNode.java new file mode 100644 index 0000000000..acad042946 --- /dev/null +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/util/WritePointerNode.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020, 2020, 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 3 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 3 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 + * 3 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.runtime.ffi.util; + +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.ValueProfile; +import com.oracle.truffle.r.runtime.DSLConfig; +import com.oracle.truffle.r.runtime.RInternalError; + +/** + * Encapsulates the operation of writing a value to a target object, which should represent a + * pointer. This node tries to write the data using array messages of the interop protocol and if + * that fails it uses the {@code toNative} and {@code asPointer} messages to get the raw pointer and + * writes to it using unsafe access. + * + * Note the {@code index} parameter determines the offset of the write the same way as in + * {@link NativeMemory#putValue(long, long, Object)}. + */ +@GenerateUncached +@ImportStatic(DSLConfig.class) +public abstract class WritePointerNode extends Node { + public static WritePointerNode create() { + return WritePointerNodeGen.create(); + } + + public final void write(Object target, Object value) { + execute(target, 0, value); + } + + public abstract void execute(Object target, int index, Object value); + + @Specialization(guards = "targetLib.isArrayElementWritable(target, index)", limit = "getInteropLibraryCacheSize()") + protected static void putInArray(Object target, int index, Object value, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + targetLib.writeArrayElement(target, index, value); + } catch (UnsupportedMessageException | UnsupportedTypeException | InvalidArrayIndexException e) { + throw RInternalError.shouldNotReachHere(); + } + } + + @Specialization(guards = {"!targetLib.isArrayElementWritable(target, index)", "targetLib.isPointer(target)"}, limit = "getInteropLibraryCacheSize()") + protected static void putIntNativeFastPath(Object target, int index, Object value, + @Cached("createClassProfile()") ValueProfile valueProfile, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + long ptr = targetLib.asPointer(target); + NativeMemory.putValue(ptr, index, valueProfile.profile(value)); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere(); + } + } + + @Specialization(guards = {"!targetLib.isArrayElementWritable(target, index)", "!targetLib.isPointer(target)"}, limit = "getInteropLibraryCacheSize()") + protected static void putIntNative(Object target, int index, Object value, + @Cached("createClassProfile()") ValueProfile valueProfile, + @CachedLibrary("target") InteropLibrary targetLib) { + try { + targetLib.toNative(target); + long ptr = targetLib.asPointer(target); + NativeMemory.putValue(ptr, index, valueProfile.profile(value)); + } catch (UnsupportedMessageException e) { + throw RInternalError.shouldNotReachHere(); + } + } +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/runtime/ffi/NativeArrayTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/runtime/ffi/NativeArrayTests.java new file mode 100644 index 0000000000..4115251645 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/runtime/ffi/NativeArrayTests.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020, 2020, 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 3 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 3 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 + * 3 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.test.runtime.ffi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RStringVector; +import com.oracle.truffle.r.runtime.ffi.interop.NativeCharArray; +import com.oracle.truffle.r.runtime.ffi.interop.NativeDoubleArray; +import com.oracle.truffle.r.runtime.ffi.interop.NativeIntegerArray; +import com.oracle.truffle.r.runtime.ffi.interop.StringArrayWrapper; +import com.oracle.truffle.r.runtime.ffi.util.NativeMemory; +import org.junit.Test; + +public class NativeArrayTests { + private static final double DELTA = 0.0000000001; + private final InteropLibrary interop = InteropLibrary.getFactory().getUncached(); + + @Test + public void testIntArray() throws Throwable { + int[] array = new int[]{1, 2, 3}; + NativeIntegerArray wrapper = new NativeIntegerArray(array); + + // interop array operations + assertEquals(1, interop.readArrayElement(wrapper, 0)); + interop.writeArrayElement(wrapper, 0, 42); + assertEquals(42, interop.readArrayElement(wrapper, 0)); + + // pointers + assertFalse(interop.isPointer(wrapper)); + interop.toNative(wrapper); + long address = interop.asPointer(wrapper); + NativeMemory.putInt(address, 0, 11); + NativeMemory.putInt(address, 2, 13); + assertEquals(11, interop.readArrayElement(wrapper, 0)); + assertEquals(13, interop.readArrayElement(wrapper, 2)); + + // transfer back to managed memory + int[] result = (int[]) wrapper.refresh(); + assertEquals(11, result[0]); + assertEquals(2, result[1]); + assertEquals(13, result[2]); + } + + @Test + public void testDoubleArray() throws Throwable { + double[] array = new double[]{1.5, 2.5, 3.25}; + NativeDoubleArray wrapper = new NativeDoubleArray(array); + + // interop array operations + assertEquals(1.5, interop.readArrayElement(wrapper, 0)); + interop.writeArrayElement(wrapper, 0, 4.5); + assertEquals(4.5, interop.readArrayElement(wrapper, 0)); + + // pointers + assertFalse(interop.isPointer(wrapper)); + interop.toNative(wrapper); + long address = interop.asPointer(wrapper); + NativeMemory.putDouble(address, 0, 5.5); + NativeMemory.putDouble(address, 2, 6.5); + assertEquals(5.5, interop.readArrayElement(wrapper, 0)); + assertEquals(6.5, interop.readArrayElement(wrapper, 2)); + + // transfer back to managed memory + double[] result = (double[]) wrapper.refresh(); + assertEquals(5.5, result[0], DELTA); + assertEquals(2.5, result[1], DELTA); + assertEquals(6.5, result[2], DELTA); + } + + @Test + public void testCharArray() throws Throwable { + byte[] bytes = "test".getBytes(); + NativeCharArray wrapper = new NativeCharArray(bytes); + + // interop array operations + assertEquals((byte) 't', interop.readArrayElement(wrapper, 0)); + assertEquals((byte) 0, interop.readArrayElement(wrapper, 4)); + interop.writeArrayElement(wrapper, 0, (byte) 'c'); + assertEquals((byte) 'c', interop.readArrayElement(wrapper, 0)); + + // pointers + assertFalse(interop.isPointer(wrapper)); + interop.toNative(wrapper); + long address = interop.asPointer(wrapper); + NativeMemory.putByte(address, 0, (byte) 'n'); + NativeMemory.putByte(address, 2, (byte) 'e'); + assertEquals((byte) 'n', interop.readArrayElement(wrapper, 0)); + assertEquals((byte) 'e', interop.readArrayElement(wrapper, 2)); + assertEquals((byte) 0, interop.readArrayElement(wrapper, 4)); + + // transfer back to native memory + byte[] result = (byte[]) wrapper.refresh(); + assertEquals((byte) 'n', result[0]); + assertEquals((byte) 'e', result[1]); + assertEquals((byte) 'e', result[2]); + assertEquals((byte) 't', result[3]); + } + + @Test + public void testStringArray() throws Throwable { + RStringVector vector = RDataFactory.createStringVector(new String[]{"a", "bcde"}, true); + StringArrayWrapper wrapper = new StringArrayWrapper(vector); + + // interop array operations + NativeCharArray item0 = (NativeCharArray) interop.readArrayElement(wrapper, 0); + assertEquals("a", item0.getString()); + + NativeCharArray item1 = (NativeCharArray) interop.readArrayElement(wrapper, 1); + assertEquals("bcde", item1.getString()); + + // no support for writing via interop, TODO: issue? + + // pointers + assertFalse(interop.isPointer(wrapper)); + interop.toNative(wrapper); + long address = interop.asPointer(wrapper); + + long item0Addr = NativeMemory.getLong(address); + assertEquals((byte) 'a', NativeMemory.getByte(item0Addr, 0)); + assertEquals((byte) 0, NativeMemory.getByte(item0Addr, 1)); + + long item1Addr = NativeMemory.getLong(address, 1); + assertEquals((byte) 'b', NativeMemory.getByte(item1Addr, 0)); + assertEquals((byte) 'c', NativeMemory.getByte(item1Addr, 1)); + assertEquals((byte) 'd', NativeMemory.getByte(item1Addr, 2)); + assertEquals((byte) 'e', NativeMemory.getByte(item1Addr, 3)); + assertEquals((byte) 0, NativeMemory.getByte(item1Addr, 4)); + + NativeMemory.putByte(item0Addr, 0, (byte) 'q'); + + // transfer back to native memory + RStringVector result = wrapper.copyBackFromNative(); + assertEquals("q", result.getDataAt(0)); + assertEquals("bcde", result.getDataAt(1)); + } +}