Skip to content

Commit 07ebf50

Browse files
committed
Tagged pointers for native.
PullRequest: truffleruby/501
2 parents 3fa612b + 517f020 commit 07ebf50

File tree

5 files changed

+120
-69
lines changed

5 files changed

+120
-69
lines changed

src/main/java/org/truffleruby/cext/UnwrapNode.java

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package org.truffleruby.cext;
22

3+
import static org.truffleruby.cext.ValueWrapperManager.TAG_MASK;
4+
import static org.truffleruby.cext.ValueWrapperManager.LONG_TAG;
5+
import static org.truffleruby.cext.ValueWrapperManager.OBJECT_TAG;
6+
import static org.truffleruby.cext.ValueWrapperManager.FALSE_HANDLE;
7+
38
import org.truffleruby.Layouts;
9+
10+
import org.truffleruby.cext.UnwrapNodeGen.UnwrapNativeNodeGen;
11+
import org.truffleruby.language.NotProvided;
412
import org.truffleruby.language.RubyBaseNode;
513
import org.truffleruby.language.control.RaiseException;
614

@@ -18,6 +26,54 @@
1826
@ImportStatic(Message.class)
1927
public abstract class UnwrapNode extends RubyBaseNode {
2028

29+
@ImportStatic(ValueWrapperManager.class)
30+
public static abstract class UnwrapNativeNode extends RubyBaseNode {
31+
32+
public abstract Object execute(long handle);
33+
34+
@Specialization(guards = "handle == FALSE_HANDLE")
35+
public boolean unwrapFalse(long handle) {
36+
return false;
37+
}
38+
39+
@Specialization(guards = "handle == TRUE_HANDLE")
40+
public boolean unwrapTrue(long handle) {
41+
return true;
42+
}
43+
44+
@Specialization(guards = "handle == UNDEF_HANDLE")
45+
public NotProvided unwrapUndef(long handle) {
46+
return NotProvided.INSTANCE;
47+
}
48+
49+
@Specialization(guards = "handle == NIL_HANDLE")
50+
public DynamicObject unwrapNil(long handle) {
51+
return nil();
52+
}
53+
54+
@Specialization(guards = "isTaggedLong(handle)")
55+
public long unwrapTaggedLong(long handle) {
56+
return handle >> 1;
57+
}
58+
59+
@Specialization(guards = "isTaggedObject(handle)")
60+
public Object unwrapTaggedObject(long handle) {
61+
return getContext().getValueWrapperManager().getFromHandleMap(handle);
62+
}
63+
64+
public boolean isTaggedLong(long handle) {
65+
return (handle & LONG_TAG) == LONG_TAG;
66+
}
67+
68+
public boolean isTaggedObject(long handle) {
69+
return handle != FALSE_HANDLE && (handle & TAG_MASK) == OBJECT_TAG;
70+
}
71+
72+
public static UnwrapNativeNode create() {
73+
return UnwrapNativeNodeGen.create();
74+
}
75+
}
76+
2177
public abstract Object execute(Object value);
2278

2379
@Specialization(guards = "isWrapper(value)")
@@ -29,6 +85,7 @@ public Object unwrapValue(DynamicObject value) {
2985
public Object unwrapTypeCastObject(TruffleObject value,
3086
@Cached("IS_POINTER.createNode()") Node isPointerNode,
3187
@Cached("AS_POINTER.createNode()") Node asPointerNode,
88+
@Cached("create()") UnwrapNativeNode unwrapNativeNode,
3289
@Cached("create()") BranchProfile unsupportedProfile,
3390
@Cached("create()") BranchProfile nonPointerProfile) {
3491
if (ForeignAccess.sendIsPointer(isPointerNode, value)) {
@@ -39,17 +96,13 @@ public Object unwrapTypeCastObject(TruffleObject value,
3996
unsupportedProfile.enter();
4097
throw new RaiseException(getContext(), coreExceptions().argumentError(e.getMessage(), this, e));
4198
}
42-
return unwrapHandle(handle);
99+
return unwrapNativeNode.execute(handle);
43100
} else {
44101
nonPointerProfile.enter();
45102
throw new RaiseException(getContext(), coreExceptions().argumentError("Not a handle or a pointer", this));
46103
}
47104
}
48105

49-
private Object unwrapHandle(long handle) {
50-
return getContext().getValueWrapperManager().getFromHandleMap(handle);
51-
}
52-
53106
public static boolean isWrapper(TruffleObject value) {
54107
return ValueWrapperObjectType.isInstance(value);
55108
}

src/main/java/org/truffleruby/cext/ValueWrapperManager.java

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,30 @@
1515
import org.truffleruby.RubyContext;
1616
import org.truffleruby.collections.LongHashMap;
1717
import org.truffleruby.extra.ffi.Pointer;
18-
import org.truffleruby.language.NotProvided;
19-
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
2018
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2119
import com.oracle.truffle.api.object.DynamicObject;
2220

2321
public class ValueWrapperManager {
2422

2523
static final int UNSET_HANDLE = -1;
2624

27-
@CompilationFinal private DynamicObject undefWrapper = null;
28-
@CompilationFinal private DynamicObject trueWrapper = null;
29-
@CompilationFinal private DynamicObject falseWrapper = null;
25+
/*
26+
* These constants are taken from ruby.h, and are based on us not tagging doubles.
27+
*/
28+
29+
public static final int FALSE_HANDLE = 0b000;
30+
public static final int TRUE_HANDLE = 0b010;
31+
public static final int NIL_HANDLE = 0b100;
32+
public static final int UNDEF_HANDLE = 0b110;
33+
34+
public static final long LONG_TAG = 1;
35+
public static final long OBJECT_TAG = 0;
36+
37+
public static final long MIN_FIXNUM_VALUE = -(1L << 62);
38+
public static final long MAX_FIXNUM_VALUE = (1L << 62) - 1;
39+
40+
public static final long TAG_MASK = 0b111;
3041

31-
private final LongHashMap<DynamicObject> longMap = new LongHashMap<>(128);
3242
private final LongHashMap<WeakReference<DynamicObject>> handleMap = new LongHashMap<>(1024);
3343

3444
private final RubyContext context;
@@ -37,48 +47,17 @@ public ValueWrapperManager(RubyContext context) {
3747
this.context = context;
3848
}
3949

40-
public DynamicObject undefWrapper() {
41-
if (undefWrapper == null) {
42-
undefWrapper = Layouts.VALUE_WRAPPER.createValueWrapper(NotProvided.INSTANCE, UNSET_HANDLE);
43-
}
44-
return undefWrapper;
45-
}
46-
47-
public DynamicObject booleanWrapper(boolean value) {
48-
if (value) {
49-
if (trueWrapper == null) {
50-
trueWrapper = Layouts.VALUE_WRAPPER.createValueWrapper(true, UNSET_HANDLE);
51-
}
52-
return trueWrapper;
53-
} else {
54-
if (falseWrapper == null) {
55-
falseWrapper = createFalseWrapper();
56-
}
57-
return falseWrapper;
58-
}
59-
}
60-
61-
private DynamicObject createFalseWrapper() {
62-
// Ensure that Qfalse will by falsy in C.
63-
return Layouts.VALUE_WRAPPER.createValueWrapper(false, 0);
64-
}
65-
6650
/*
6751
* We keep a map of long wrappers that have been generated because various C extensions assume
6852
* that any given fixnum will translate to a given VALUE.
6953
*/
7054
@TruffleBoundary
7155
public synchronized DynamicObject longWrapper(long value) {
72-
DynamicObject wrapper = longMap.get(value);
73-
if (wrapper == null) {
74-
wrapper = Layouts.VALUE_WRAPPER.createValueWrapper(value, ValueWrapperManager.UNSET_HANDLE);
75-
longMap.put(value, wrapper);
76-
}
77-
return wrapper;
56+
return Layouts.VALUE_WRAPPER.createValueWrapper(value, UNSET_HANDLE);
7857
}
7958

8059
public DynamicObject doubleWrapper(double value) {
81-
return Layouts.VALUE_WRAPPER.createValueWrapper(value, ValueWrapperManager.UNSET_HANDLE);
60+
return Layouts.VALUE_WRAPPER.createValueWrapper(value, UNSET_HANDLE);
8261
}
8362

8463
@TruffleBoundary
@@ -108,7 +87,7 @@ public synchronized void removeFromHandleMap(long handle) {
10887
public synchronized long createNativeHandle(DynamicObject wrapper) {
10988
Pointer handlePointer = Pointer.malloc(1);
11089
long handleAddress = handlePointer.getAddress();
111-
if ((handleAddress & 0x7) != 0) {
90+
if ((handleAddress & TAG_MASK) != 0) {
11291
throw new RuntimeException("unaligned malloc for native handle");
11392
}
11493
Layouts.VALUE_WRAPPER.setHandle(wrapper, handleAddress);

src/main/java/org/truffleruby/cext/WrapNode.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package org.truffleruby.cext;
22

3+
import static org.truffleruby.cext.ValueWrapperManager.FALSE_HANDLE;
4+
import static org.truffleruby.cext.ValueWrapperManager.TRUE_HANDLE;
5+
import static org.truffleruby.cext.ValueWrapperManager.LONG_TAG;
6+
import static org.truffleruby.cext.ValueWrapperManager.NIL_HANDLE;
7+
import static org.truffleruby.cext.ValueWrapperManager.UNDEF_HANDLE;
8+
39
import org.jcodings.specific.UTF8Encoding;
410
import org.truffleruby.Layouts;
511
import org.truffleruby.core.rope.RopeOperations;
@@ -15,19 +21,22 @@
1521
import com.oracle.truffle.api.dsl.Specialization;
1622
import com.oracle.truffle.api.interop.TruffleObject;
1723
import com.oracle.truffle.api.object.DynamicObject;
24+
import com.oracle.truffle.api.profiles.BranchProfile;
1825

1926
public abstract class WrapNode extends RubyBaseNode {
2027

2128
public abstract TruffleObject execute(Object value);
2229

2330
@Specialization
24-
public DynamicObject wrapInt(int value) {
25-
return getContext().getValueWrapperManager().longWrapper(value);
26-
}
27-
28-
@Specialization
29-
public DynamicObject wrapLong(long value) {
30-
return getContext().getValueWrapperManager().longWrapper(value);
31+
public DynamicObject wrapLong(long value,
32+
@Cached("create()") BranchProfile smallFixnumProfile) {
33+
if (value >= ValueWrapperManager.MIN_FIXNUM_VALUE && value <= ValueWrapperManager.MAX_FIXNUM_VALUE) {
34+
smallFixnumProfile.enter();
35+
long val = (value << 1) | LONG_TAG;
36+
return Layouts.VALUE_WRAPPER.createValueWrapper(value, val);
37+
} else {
38+
return getContext().getValueWrapperManager().longWrapper(value);
39+
}
3140
}
3241

3342
@Specialization
@@ -37,25 +46,32 @@ public DynamicObject wrapDouble(double value) {
3746

3847
@Specialization
3948
public DynamicObject wrapBoolean(boolean value) {
40-
return getContext().getValueWrapperManager().booleanWrapper(value);
49+
return Layouts.VALUE_WRAPPER.createValueWrapper(value, value ? TRUE_HANDLE : FALSE_HANDLE);
4150
}
4251

4352
@Specialization
4453
public DynamicObject wrapUndef(NotProvided value) {
45-
return getContext().getValueWrapperManager().undefWrapper();
54+
return Layouts.VALUE_WRAPPER.createValueWrapper(value, UNDEF_HANDLE);
4655
}
4756

4857
@Specialization(guards = "isWrapped(value)")
4958
public DynamicObject wrapWrappedValue(DynamicObject value) {
5059
throw new RaiseException(getContext(), coreExceptions().argumentError(RopeOperations.encodeAscii("Wrapping wrapped object", UTF8Encoding.INSTANCE), this));
5160
}
5261

53-
@Specialization(guards = "isRubyBasicObject(value)")
62+
@Specialization(guards = "isNil(value)")
63+
public DynamicObject wrapNil(DynamicObject value) {
64+
return Layouts.VALUE_WRAPPER.createValueWrapper(nil(), NIL_HANDLE);
65+
}
66+
67+
@Specialization(guards = { "isRubyBasicObject(value)", "!isNil(value)" })
5468
public DynamicObject wrapValue(DynamicObject value,
5569
@Cached("createReader()") ReadObjectFieldNode readWrapperNode,
56-
@Cached("createWriter()") WriteObjectFieldNode writeWrapperNode) {
70+
@Cached("createWriter()") WriteObjectFieldNode writeWrapperNode,
71+
@Cached("create()") BranchProfile noHandleProfile) {
5772
DynamicObject wrapper = (DynamicObject) readWrapperNode.execute(value);
5873
if (wrapper == null) {
74+
noHandleProfile.enter();
5975
synchronized (value) {
6076
wrapper = (DynamicObject) readWrapperNode.execute(value);
6177
if (wrapper == null) {

src/main/java/org/truffleruby/core/objectspace/ObjectSpaceManager.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
import com.oracle.truffle.api.utilities.CyclicAssumption;
4545

4646
import org.truffleruby.RubyContext;
47-
import org.truffleruby.language.objects.ObjectIDOperations;
47+
import org.truffleruby.cext.ValueWrapperManager;
4848

4949
import java.lang.management.GarbageCollectorMXBean;
5050
import java.lang.management.ManagementFactory;
@@ -63,7 +63,7 @@ public class ObjectSpaceManager {
6363
private int tracingAssumptionActivations = 0;
6464
private boolean tracingPaused = false;
6565

66-
private final AtomicLong nextObjectID = new AtomicLong(ObjectIDOperations.FIRST_OBJECT_ID);
66+
private final AtomicLong nextObjectID = new AtomicLong(ValueWrapperManager.TAG_MASK + 1);
6767

6868
public ObjectSpaceManager(RubyContext context) {
6969
this.context = context;
@@ -110,9 +110,9 @@ public boolean isTracing() {
110110
}
111111

112112
public long getNextObjectID() {
113-
final long id = nextObjectID.getAndAdd(2);
113+
final long id = nextObjectID.getAndAdd(ValueWrapperManager.TAG_MASK + 1);
114114

115-
if (id < 0) {
115+
if (id == 0) {
116116
CompilerDirectives.transferToInterpreterAndInvalidate();
117117
throw new RuntimeException("Object IDs exhausted");
118118
}

src/main/java/org/truffleruby/language/objects/ObjectIDOperations.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,16 @@
1414
import com.oracle.truffle.api.object.Property;
1515
import org.truffleruby.Layouts;
1616
import org.truffleruby.RubyContext;
17+
import org.truffleruby.cext.ValueWrapperManager;
1718
import org.truffleruby.core.numeric.BignumOperations;
1819
import org.truffleruby.language.objects.shared.SharedObjects;
1920

21+
import static org.truffleruby.cext.ValueWrapperManager.FALSE_HANDLE;
22+
import static org.truffleruby.cext.ValueWrapperManager.MAX_FIXNUM_VALUE;
23+
import static org.truffleruby.cext.ValueWrapperManager.MIN_FIXNUM_VALUE;
24+
import static org.truffleruby.cext.ValueWrapperManager.NIL_HANDLE;
25+
import static org.truffleruby.cext.ValueWrapperManager.TRUE_HANDLE;
26+
2027
import java.math.BigInteger;
2128

2229
/**
@@ -41,22 +48,18 @@
4148
*/
4249
public abstract class ObjectIDOperations {
4350

44-
public static final long FALSE = 0;
45-
public static final long TRUE = 2;
46-
public static final long NIL = 4;
47-
public static final long FIRST_OBJECT_ID = 6;
51+
public static final long FALSE = FALSE_HANDLE;
52+
public static final long TRUE = TRUE_HANDLE;
53+
public static final long NIL = NIL_HANDLE;
4854

4955
private static final BigInteger LARGE_FIXNUM_FLAG = BigInteger.ONE.shiftLeft(64);
5056
private static final BigInteger FLOAT_FLAG = BigInteger.ONE.shiftLeft(65);
5157

52-
private static final long SMALL_FIXNUM_MIN = -(1L << 62);
53-
private static final long SMALL_FIXNUM_MAX = (1L << 62) - 1;
54-
5558
// primitive => ID
5659

5760
public static boolean isSmallFixnum(long fixnum) {
5861
// TODO: optimize
59-
return SMALL_FIXNUM_MIN <= fixnum && fixnum <= SMALL_FIXNUM_MAX;
62+
return MIN_FIXNUM_VALUE <= fixnum && fixnum <= MAX_FIXNUM_VALUE;
6063
}
6164

6265
public static long smallFixnumToIDOverflow(long fixnum) throws ArithmeticException {
@@ -99,7 +102,7 @@ public static boolean isFloatID(BigInteger id) {
99102
}
100103

101104
public static boolean isBasicObjectID(long id) {
102-
return id >= FIRST_OBJECT_ID && id % 2 == 0;
105+
return id != 0 && (id & ValueWrapperManager.TAG_MASK) == 0;
103106
}
104107

105108
private static BigInteger unsignedBigInteger(long value) {

0 commit comments

Comments
 (0)