Skip to content

Commit 746aa35

Browse files
committed
Eliminate arg array assembly in sub call code.
Instead, we use invokedynamic to do it, which reduces the amount of code we generate for sub argument passing. Also should hopefully give better performance, since we're delegating this assembly work to the combinators provided by the JVM, which presumably it knows how to work with efficiently.
1 parent 55e954c commit 746aa35

File tree

2 files changed

+92
-23
lines changed

2 files changed

+92
-23
lines changed

src/vm/jvm/QAST/Compiler.nqp

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,22 +1102,83 @@ sub process_args($qastcomp, @children, $il, $first, :$inv_temp) {
11021102
# Return callsite index (which may create it if needed).
11031103
return [$*CODEREFS.get_callsite_idx(@callsite, @argnames), $arg_array];
11041104
}
1105+
sub process_args_onto_stack($qastcomp, @children, $il, $first, :$inv_temp) {
1106+
# Make sure we do positionals before nameds.
1107+
my @pos;
1108+
my @named;
1109+
for @children {
1110+
nqp::push(($_.named ?? @named !! @pos), $_);
1111+
}
1112+
my @order := @pos;
1113+
for @named { nqp::push(@order, $_) }
1114+
1115+
# Process the arguments, computing each of them and putting them onto the
1116+
# stack.
1117+
my @arg_results;
1118+
my @arg_jtypes := [$TYPE_TC];
1119+
my @callsite;
1120+
my @argnames;
1121+
my int $i := $first;
1122+
while $i < +@order {
1123+
my $arg_res := $qastcomp.as_jast(@order[$i]);
1124+
$il.append($arg_res.jast);
1125+
nqp::push(@arg_results, $arg_res);
1126+
1127+
my int $type := $arg_res.type;
1128+
if $type == $RT_INT {
1129+
nqp::push(@arg_jtypes, 'J');
1130+
}
1131+
elsif $type == $RT_NUM {
1132+
nqp::push(@arg_jtypes, 'D');
1133+
}
1134+
else {
1135+
nqp::push(@arg_jtypes, jtype($arg_res.type));
1136+
}
1137+
1138+
if $i == $first && $inv_temp {
1139+
if $type == $RT_OBJ {
1140+
$il.append(JAST::Instruction.new( :op('dup') ));
1141+
$il.append(JAST::Instruction.new( :op('astore'), $inv_temp ));
1142+
}
1143+
else {
1144+
nqp::die("Invocant must be an object");
1145+
}
1146+
}
1147+
1148+
my int $flags := 0;
1149+
if @order[$i].flat {
1150+
$flags := @order[$i].named ?? 24 !! 16;
1151+
}
1152+
elsif @order[$i].named -> $name {
1153+
$flags := 8;
1154+
nqp::push(@argnames, $name);
1155+
}
1156+
nqp::push(@callsite, arg_type($type) + $flags);
1157+
1158+
$i++;
1159+
}
1160+
1161+
# Return callsite index (which may create it if needed).
1162+
return [$*CODEREFS.get_callsite_idx(@callsite, @argnames), @arg_results, @arg_jtypes];
1163+
}
11051164
QAST::OperationsJAST.add_core_op('call', sub ($qastcomp, $node) {
11061165
my $il := JAST::InstructionList.new();
11071166

11081167
# If it's a direct call, then use invokedynamic to resolve the name in
11091168
# the current lexical scope.
11101169
if $node.name ne "" {
1111-
# Process arguments.
1112-
my @argstuff := process_args($qastcomp, @($node), $il, $node.name eq "" ?? 1 !! 0);
1170+
# Process arguments and force them into locals.
1171+
my @argstuff := process_args_onto_stack($qastcomp, @($node), $il, $node.name eq "" ?? 1 !! 0);
11131172
my $cs_idx := @argstuff[0];
1173+
$*STACK.spill_to_locals($il);
11141174

1115-
# Emit the call. Note, name passed as extra arg as some valid
1116-
# names in Perl 6 are not valid method names on the JVM.
1175+
# Emit the call. Note, name passed as extra arg as some valid names in
1176+
# Perl 6 are not valid method names on the JVM. We use the fact that
1177+
# the stack was spilled to sneak the ThreadContext arg in.
11171178
$il.append(JAST::Instruction.new( :op('aload_1') ));
1118-
$il.append(JAST::Instruction.new( :op('aload'), @argstuff[1] ));
1179+
$*STACK.obtain($il, |@argstuff[1]) if @argstuff[1];
11191180
$il.append(JAST::InvokeDynamic.new(
1120-
'subcall', 'V', [$TYPE_TC, "[$TYPE_OBJ"],
1181+
'subcall', 'V', @argstuff[2],
11211182
'org/perl6/nqp/runtime/IndyBootstrap', 'subcall',
11221183
[
11231184
JAST::PushSVal.new( :value($node.name) ),
@@ -2457,9 +2518,11 @@ class QAST::CompilerJAST {
24572518
return 1;
24582519
}
24592520

2460-
# Mix of local and stack is not yet supported.
2521+
# Mix of local and stack: just spill everything still on the
2522+
# stack, and try again.
24612523
else {
2462-
nqp::die("Mix of local and stack items in obtain NYI");
2524+
self.spill_to_locals($il);
2525+
return self.obtain($il, |@things);
24632526
}
24642527
}
24652528

src/vm/jvm/runtime/org/perl6/nqp/runtime/IndyBootstrap.java

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ public static CallSite subcall(Lookup caller, String _, MethodType type, String
5252
/* Create a mutable callsite, and curry the resolver with it and
5353
* the sub name. */
5454
MutableCallSite cs = new MutableCallSite(type);
55-
cs.setTarget(MethodHandles.insertArguments(res, 0, caller, cs, name, csIdx));
55+
cs.setTarget(MethodHandles
56+
.insertArguments(res, 0, caller, cs, name, csIdx)
57+
.asCollector(Object[].class, type.parameterCount() - 1)
58+
.asType(type));
5659

5760
/* Produce callsite; it'll be updated with the resolved call upon the
5861
* first invocation. */
@@ -63,7 +66,7 @@ public static CallSite subcall(Lookup caller, String _, MethodType type, String
6366
}
6467
}
6568

66-
public static void subcallResolve(Lookup caller, MutableCallSite cs, String name, int csIdx, ThreadContext tc, Object[] args) {
69+
public static void subcallResolve(Lookup caller, MutableCallSite cs, String name, int csIdx, ThreadContext tc, Object... args) {
6770
/* Locate the thing to call. */
6871
SixModelObject invokee = Ops.getlex(name, tc);
6972

@@ -84,7 +87,7 @@ public static void subcallResolve(Lookup caller, MutableCallSite cs, String name
8487
try {
8588
cs.setTarget(caller
8689
.findStatic(IndyBootstrap.class, "lexotic_o",
87-
MethodType.methodType(long.class, ThreadContext.class, Object[].class))
90+
MethodType.methodType(long.class, ThreadContext.class, SixModelObject.class))
8891
.bindTo(throwee.target));
8992
}
9093
catch (Exception e) {
@@ -97,7 +100,7 @@ public static void subcallResolve(Lookup caller, MutableCallSite cs, String name
97100
try {
98101
cs.setTarget(MethodHandles.insertArguments(
99102
caller.findStatic(IndyBootstrap.class, "lexotic_i",
100-
MethodType.methodType(long.class, ThreadContext.class, Object[].class)),
103+
MethodType.methodType(long.class, ThreadContext.class, long.class)),
101104
0, throwee.target, intBoxType));
102105
}
103106
catch (Exception e) {
@@ -110,7 +113,7 @@ public static void subcallResolve(Lookup caller, MutableCallSite cs, String name
110113
try {
111114
cs.setTarget(MethodHandles.insertArguments(
112115
caller.findStatic(IndyBootstrap.class, "lexotic_n",
113-
MethodType.methodType(long.class, ThreadContext.class, Object[].class)),
116+
MethodType.methodType(long.class, ThreadContext.class, double.class)),
114117
0, throwee.target, numBoxType));
115118
}
116119
catch (Exception e) {
@@ -123,7 +126,7 @@ public static void subcallResolve(Lookup caller, MutableCallSite cs, String name
123126
try {
124127
cs.setTarget(MethodHandles.insertArguments(
125128
caller.findStatic(IndyBootstrap.class, "lexotic_s",
126-
MethodType.methodType(long.class, ThreadContext.class, Object[].class)),
129+
MethodType.methodType(long.class, ThreadContext.class, String.class)),
127130
0, throwee.target, strBoxType));
128131
}
129132
catch (Exception e) {
@@ -153,7 +156,10 @@ public static void subcallResolve(Lookup caller, MutableCallSite cs, String name
153156

154157
/* Now need to adapt to the target callsite by binding the CodeRef
155158
* and callsite with what they've been resolved to. */
156-
cs.setTarget(MethodHandles.insertArguments(cr.staticInfo.mh, 1, cr, csd));
159+
cs.setTarget(MethodHandles
160+
.insertArguments(cr.staticInfo.mh, 1, cr, csd)
161+
.asVarargsCollector(Object[].class)
162+
.asType(cs.getTarget().type()));
157163

158164
/* Make the sub call directly for this initial call. */
159165
try {
@@ -167,31 +173,31 @@ public static void subcallResolve(Lookup caller, MutableCallSite cs, String name
167173
}
168174
}
169175

170-
public static void lexotic_o(long target, ThreadContext tc, Object[] args) {
176+
public static void lexotic_o(long target, ThreadContext tc, SixModelObject arg) {
171177
LexoticException throwee = tc.theLexotic;
172178
throwee.target = target;
173-
throwee.payload = (SixModelObject)args[0];
179+
throwee.payload = arg;
174180
throw throwee;
175181
}
176182

177-
public static void lexotic_i(long target, SixModelObject boxType, ThreadContext tc, Object[] args) {
183+
public static void lexotic_i(long target, SixModelObject boxType, ThreadContext tc, long arg) {
178184
LexoticException throwee = tc.theLexotic;
179185
throwee.target = target;
180-
throwee.payload = Ops.box_i((long)args[0], boxType, tc);
186+
throwee.payload = Ops.box_i(arg, boxType, tc);
181187
throw throwee;
182188
}
183189

184-
public static void lexotic_n(long target, SixModelObject boxType, ThreadContext tc, Object[] args) {
190+
public static void lexotic_n(long target, SixModelObject boxType, ThreadContext tc, double arg) {
185191
LexoticException throwee = tc.theLexotic;
186192
throwee.target = target;
187-
throwee.payload = Ops.box_n((double)args[0], boxType, tc);
193+
throwee.payload = Ops.box_n(arg, boxType, tc);
188194
throw throwee;
189195
}
190196

191-
public static void lexotic_s(long target, SixModelObject boxType, ThreadContext tc, Object[] args) {
197+
public static void lexotic_s(long target, SixModelObject boxType, ThreadContext tc, String arg) {
192198
LexoticException throwee = tc.theLexotic;
193199
throwee.target = target;
194-
throwee.payload = Ops.box_s((String)args[0], boxType, tc);
200+
throwee.payload = Ops.box_s(arg, boxType, tc);
195201
throw throwee;
196202
}
197203
}

0 commit comments

Comments
 (0)