Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4d9c129c184f
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 276a7d220518
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on May 20, 2016

  1. Unify super and call splatting logic in interpreter and JIT.

    This improves things in a few ways:
    
    * JIT and interpreter are calling through most of the same APIs.
    * Fewer transient objects for calls with splats.
    * Less duplication of code.
    
    There are other ways we could split the super dispatch paths
    apart to reduce overhead and allocation. This patch puts us on
    that path.
    headius committed May 20, 2016
    Copy the full SHA
    32512d5 View commit details

Commits on May 26, 2016

  1. Merge pull request #3911 from headius/zsuper_splatmap

    Unify super and call splatting logic in interpreter and JIT.
    headius committed May 26, 2016
    Copy the full SHA
    276a7d2 View commit details
32 changes: 12 additions & 20 deletions core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ public abstract class CallBase extends NOperandInstr implements ClosureAccepting
private transient boolean targetRequiresCallersBinding; // Does this call make use of the caller's binding?
private transient boolean targetRequiresCallersFrame; // Does this call make use of the caller's frame?
private transient boolean dontInline;
private transient boolean containsArgSplat;
private transient boolean[] splatMap;
private transient boolean procNew;
private boolean potentiallyRefined;

@@ -48,7 +48,7 @@ protected CallBase(Operation op, CallType callType, String name, Operand receive
this.name = name;
this.callType = callType;
this.callSite = getCallSiteFor(callType, name, potentiallyRefined);
containsArgSplat = containsArgSplat(args);
splatMap = IRRuntimeHelpers.buildSplatMap(args);
flagsComputed = false;
canBeEval = true;
targetRequiresCallersBinding = true;
@@ -128,8 +128,8 @@ public CallType getCallType() {
return callType;
}

public boolean containsArgSplat() {
return containsArgSplat;
public boolean[] splatMap() {
return splatMap;
}

public void setProcNew(boolean procNew) {
@@ -231,8 +231,8 @@ public boolean computeScopeFlags(IRScope scope) {
public void simplifyOperands(Map<Operand, Operand> valueMap, boolean force) {
super.simplifyOperands(valueMap, force);

// Recompute containsArgSplat flag
containsArgSplat = containsArgSplat(operands); // also checking receiver but receiver can never be a splat
// Recompute splatMap
splatMap = IRRuntimeHelpers.buildSplatMap(getCallArgs()); // also checking receiver but receiver can never be a splat
flagsComputed = false; // Forces recomputation of flags
}

@@ -424,7 +424,7 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
}

protected IRubyObject[] prepareArguments(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope dynamicScope, Object[] temp) {
return containsArgSplat ?
return splatMap != null ?
prepareArgumentsComplex(context, self, currScope, dynamicScope, temp) :
prepareArgumentsSimple(context, self, currScope, dynamicScope, temp);
}
@@ -445,20 +445,12 @@ protected IRubyObject[] prepareArgumentsComplex(ThreadContext context, IRubyObje
// But when zsuper is converted to SuperInstr with known args, splats can appear anywhere
// in the list. So, this looping handles both these scenarios, although if we wanted to
// optimize for CallInstr which has splats only in the first position, we could do that.
List<IRubyObject> argList = new ArrayList<>(argsCount * 2);
for (int i = 0; i < argsCount; i++) { // receiver is operands[0]
IRubyObject rArg = (IRubyObject) operands[i+1].retrieve(context, self, currScope, currDynScope, temp);
if (operands[i+1] instanceof Splat) {
RubyArray array = (RubyArray) rArg;
for (int j = 0; j < array.size(); j++) {
argList.add(array.eltOk(j));
}
} else {
argList.add(rArg);
}
}

return argList.toArray(new IRubyObject[argList.size()]);
// CON: Using same logic as super splatting, but this will at least only allocate at
// most two "carrier" arrays.
return IRRuntimeHelpers.splatArguments(
prepareArgumentsSimple(context, self, currScope, currDynScope, temp),
splatMap);
}

public Block prepareBlock(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temp) {
Original file line number Diff line number Diff line change
@@ -66,8 +66,7 @@ public static ZSuperInstr decode(IRReaderDecoder d) {
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
IRubyObject[] args = prepareArguments(context, self, currScope, currDynScope, temp);
Block block = prepareBlock(context, self, currScope, currDynScope, temp);
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return IRRuntimeHelpers.unresolvedSuper(context, self, args, block);
return IRRuntimeHelpers.zSuper(context, self, args, block);
}

@Override
33 changes: 23 additions & 10 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1001,7 +1001,12 @@ public static IRubyObject unresolvedSuper(ThreadContext context, IRubyObject sel

public static IRubyObject zSuperSplatArgs(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block, boolean[] splatMap) {
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return unresolvedSuperSplatArgs(context, self, args, block, splatMap);
return unresolvedSuper(context, self, splatArguments(args, splatMap), block);
}

public static IRubyObject zSuper(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
if (block == null || !block.isGiven()) block = context.getFrameBlock();
return unresolvedSuper(context, self, args, block);
}

public static IRubyObject[] splatArguments(IRubyObject[] args, boolean[] splatMap) {
@@ -1049,26 +1054,34 @@ public static boolean[] decodeSplatmap(String splatmapString) {
}
}
} else {
splatMap = new boolean[0];
splatMap = null;
}
return splatMap;
}

public static boolean[] buildSplatMap(Operand[] args, boolean containsArgSplat) {
boolean[] splatMap = new boolean[args.length];
public static boolean[] buildSplatMap(Operand[] args) {
boolean[] splatMap = null;

if (containsArgSplat) {
for (int i = 0; i < args.length; i++) {
Operand operand = args[i];
if (operand instanceof Splat) {
splatMap[i] = true;
}
for (int i = 0; i < args.length; i++) {
Operand operand = args[i];
if (operand instanceof Splat) {
if (splatMap == null) splatMap = new boolean[args.length];
splatMap[i] = true;
}
}

return splatMap;
}

public static boolean anyTrue(boolean[] booleans) {
for (boolean b : booleans) if (b) return true;
return false;
}

public static boolean needsSplatting(boolean[] splatmap) {
return splatmap != null && splatmap.length > 0 && anyTrue(splatmap);
}

public static final Type[] typesFromSignature(Signature signature) {
Type[] types = new Type[signature.argCount()];
for (int i = 0; i < signature.argCount(); i++) {
49 changes: 30 additions & 19 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -61,6 +61,12 @@
* CON FIXME: These are all dirt-stupid impls that will not be as efficient.
*/
public class IRBytecodeAdapter6 extends IRBytecodeAdapter{

public static final String SUPER_SPLAT_UNRESOLVED = sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, JVM.OBJECT_ARRAY, Block.class, boolean[].class));
public static final String SUPER_NOSPLAT_UNRESOLVED = sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, JVM.OBJECT_ARRAY, Block.class));
public static final String SUPER_SPLAT_RESOLVED = sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, String.class, RubyModule.class, JVM.OBJECT_ARRAY, Block.class, boolean[].class));
public static final String SUPER_NOSPLAT_RESOLVED = sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, String.class, RubyModule.class, JVM.OBJECT_ARRAY, Block.class));

public IRBytecodeAdapter6(SkinnyMethodAdapter adapter, Signature signature, ClassData classData) {
super(adapter, signature, classData);
}
@@ -629,42 +635,52 @@ public void invokeSelf(String name, int arity, boolean hasClosure, CallType call
public void invokeInstanceSuper(String name, int arity, boolean hasClosure, boolean[] splatmap) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to instance super has more than " + MAX_ARGUMENTS + " arguments");

performSuper(name, arity, hasClosure, splatmap, "instanceSuperSplatArgs", false);
performSuper(name, arity, hasClosure, splatmap, "instanceSuper", "instanceSuperSplatArgs", false);
}

public void invokeClassSuper(String name, int arity, boolean hasClosure, boolean[] splatmap) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to class super has more than " + MAX_ARGUMENTS + " arguments");

performSuper(name, arity, hasClosure, splatmap, "classSuperSplatArgs", false);
performSuper(name, arity, hasClosure, splatmap, "classSuper", "classSuperSplatArgs", false);
}

public void invokeUnresolvedSuper(String name, int arity, boolean hasClosure, boolean[] splatmap) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to unresolved super has more than " + MAX_ARGUMENTS + " arguments");

performSuper(name, arity, hasClosure, splatmap, "unresolvedSuperSplatArgs", true);
performSuper(name, arity, hasClosure, splatmap, "unresolvedSuper", "unresolvedSuperSplatArgs", true);
}

public void invokeZSuper(String name, int arity, boolean hasClosure, boolean[] splatmap) {
if (arity > MAX_ARGUMENTS) throw new NotCompilableException("call to zsuper has more than " + MAX_ARGUMENTS + " arguments");

performSuper(name, arity, hasClosure, splatmap, "zSuperSplatArgs", true);
performSuper(name, arity, hasClosure, splatmap, "zSuper", "zSuperSplatArgs", true);
}

private void performSuper(String name, int arity, boolean hasClosure, boolean[] splatmap, String helperName, boolean unresolved) {
private void performSuper(String name, int arity, boolean hasClosure, boolean[] splatmap, String superHelper, String splatHelper, boolean unresolved) {
SkinnyMethodAdapter adapter2;
String incomingSig;
String outgoingSig;

boolean needsSplatting = IRRuntimeHelpers.needsSplatting(splatmap);

if (hasClosure) {
incomingSig = sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, RubyClass.class, JVM.OBJECT, arity, Block.class));
outgoingSig = unresolved ?
sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, JVM.OBJECT_ARRAY, Block.class, boolean[].class)) :
sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, String.class, RubyModule.class, JVM.OBJECT_ARRAY, Block.class, boolean[].class));
} else {
incomingSig = sig(JVM.OBJECT, params(ThreadContext.class, JVM.OBJECT, JVM.OBJECT, RubyClass.class, JVM.OBJECT, arity));
outgoingSig = unresolved ?
sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, JVM.OBJECT_ARRAY, Block.class, boolean[].class)) :
sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class, String.class, RubyModule.class, JVM.OBJECT_ARRAY, Block.class, boolean[].class));
}

if (unresolved) {
if (needsSplatting) {
outgoingSig = SUPER_SPLAT_UNRESOLVED;
} else {
outgoingSig = SUPER_NOSPLAT_UNRESOLVED;
}
} else {
if (needsSplatting) {
outgoingSig = SUPER_SPLAT_RESOLVED;
} else {
outgoingSig = SUPER_NOSPLAT_RESOLVED;
}
}

String methodName = "invokeSuper" + getClassData().callSiteCount.getAndIncrement() + ':' + JavaNameMangler.mangleMethodName(name);
@@ -690,27 +706,22 @@ private void performSuper(String name, int arity, boolean hasClosure, boolean[]
adapter2.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class));
}

if (splatmap != null || splatmap.length > 0 || anyTrue(splatmap)) {
if (needsSplatting) {
String splatmapString = IRRuntimeHelpers.encodeSplatmap(splatmap);
adapter2.ldc(splatmapString);
adapter2.invokestatic(p(IRRuntimeHelpers.class), "decodeSplatmap", sig(boolean[].class, String.class));
adapter2.invokestatic(p(IRRuntimeHelpers.class), splatHelper, outgoingSig);
} else {
adapter2.getstatic(p(IRRuntimeHelpers.class), "EMPTY_BOOLEAN_ARRAY", ci(boolean[].class));
adapter2.invokestatic(p(IRRuntimeHelpers.class), superHelper, outgoingSig);
}

adapter2.invokestatic(p(IRRuntimeHelpers.class), helperName, outgoingSig);
adapter2.areturn();
adapter2.end();

// now call it
adapter.invokestatic(getClassData().clsName, methodName, incomingSig);
}

private static boolean anyTrue(boolean[] booleans) {
for (boolean b : booleans) if (b) return true;
return false;
}

public void searchConst(String name, boolean noPrivateConsts) {
adapter.invokedynamic("searchConst", sig(JVM.OBJECT, params(ThreadContext.class, StaticScope.class)), ConstantLookupSite.BOOTSTRAP, name, noPrivateConsts ? 1 : 0);
}
21 changes: 9 additions & 12 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -1087,10 +1087,10 @@ public void ClassSuperInstr(ClassSuperInstr classsuperinstr) {
String name = classsuperinstr.getName();
Operand[] args = classsuperinstr.getCallArgs();
Operand definingModule = classsuperinstr.getDefiningModule();
boolean containsArgSplat = classsuperinstr.containsArgSplat();
boolean[] splatMap = classsuperinstr.splatMap();
Operand closure = classsuperinstr.getClosureArg(null);

superCommon(name, classsuperinstr, args, definingModule, containsArgSplat, closure);
superCommon(name, classsuperinstr, args, definingModule, splatMap, closure);
}

@Override
@@ -1331,13 +1331,13 @@ public void InstanceSuperInstr(InstanceSuperInstr instancesuperinstr) {
String name = instancesuperinstr.getName();
Operand[] args = instancesuperinstr.getCallArgs();
Operand definingModule = instancesuperinstr.getDefiningModule();
boolean containsArgSplat = instancesuperinstr.containsArgSplat();
boolean[] splatMap = instancesuperinstr.splatMap();
Operand closure = instancesuperinstr.getClosureArg(null);

superCommon(name, instancesuperinstr, args, definingModule, containsArgSplat, closure);
superCommon(name, instancesuperinstr, args, definingModule, splatMap, closure);
}

private void superCommon(String name, CallInstr instr, Operand[] args, Operand definingModule, boolean containsArgSplat, Operand closure) {
private void superCommon(String name, CallInstr instr, Operand[] args, Operand definingModule, boolean[] splatMap, Operand closure) {
IRBytecodeAdapter m = jvmMethod();
Operation operation = instr.getOperation();

@@ -1359,9 +1359,6 @@ private void superCommon(String name, CallInstr instr, Operand[] args, Operand d
visit(operand);
}

// if there's splats, provide a map and let the call site sort it out
boolean[] splatMap = IRRuntimeHelpers.buildSplatMap(args, containsArgSplat);

boolean hasClosure = closure != null;
if (hasClosure) {
m.loadContext();
@@ -2146,10 +2143,10 @@ public void UnresolvedSuperInstr(UnresolvedSuperInstr unresolvedsuperinstr) {
Operand[] args = unresolvedsuperinstr.getCallArgs();
// this would be getDefiningModule but that is not used for unresolved super
Operand definingModule = UndefinedValue.UNDEFINED;
boolean containsArgSplat = unresolvedsuperinstr.containsArgSplat();
boolean[] splatMap = unresolvedsuperinstr.splatMap();
Operand closure = unresolvedsuperinstr.getClosureArg(null);

superCommon(name, unresolvedsuperinstr, args, definingModule, containsArgSplat, closure);
superCommon(name, unresolvedsuperinstr, args, definingModule, splatMap, closure);
}

@Override
@@ -2195,10 +2192,10 @@ public void ZSuperInstr(ZSuperInstr zsuperinstr) {
Operand[] args = zsuperinstr.getCallArgs();
// this would be getDefiningModule but that is not used for unresolved super
Operand definingModule = UndefinedValue.UNDEFINED;
boolean containsArgSplat = zsuperinstr.containsArgSplat();
boolean[] splatMap = zsuperinstr.splatMap();
Operand closure = zsuperinstr.getClosureArg(null);

superCommon(name, zsuperinstr, args, definingModule, containsArgSplat, closure);
superCommon(name, zsuperinstr, args, definingModule, splatMap, closure);
}

@Override