Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Upcoming JSAPI changes embedders should start considering, when migrating to SpiderMonkey 17 (or atop already-completed migration)

159 views
Skip to first unread message

Jeff Walden

unread,
Mar 26, 2013, 3:11:06 PM3/26/13
to
SpiderMonkey's API has a lot of C-isms in it that will gradually disappear, moving forward, as we start using cleaner C++ idioms. This email announces a couple such changes, that embedders can begin reacting to now, in advance of SpiderMonkey 24 and future releases that make those changes.

The first change is the important one. The second change is less important, as the breaking change that motivates it is probably a ways off (and easily worked around when we do make it).

== Use JS::CallArgs and JS::CallArgsFromVp when writing JSNatives ==

The current JSNative signature in SpiderMonkey 17, and on trunk, is:

typedef JSBool (*JSNative)(JSContext *cx, unsigned argc, jsval *vp);

Every call to a JSNative has to push three (count 'em) arguments. Every argument push has a cost when JITting, or when calling from compiled code. It was far worse back in the JSNative+JSFastNative days, to be sure, when JSNative took five (?) arguments, but even three's still too many.

We'd like to cut JSNative down to one argument, but I doubt we'll get there by SpiderMonkey 24. There's a decent change we'll get it to two arguments, tho -- by combining argc/vp into one argument: a const JS::CallArgs&. This'll be a single pointer value (as a reference, for readability), taking us down to two arguments.

JS::CallArgs is a very minimal struct encapsulating argc/vp and providing access to the stuff in vp. It's basically a C++ version of the JS_THIS_VALUE, JS_THIS_OBJECT, JS_ARGV, JS_RVAL, JS_SET_RVAL, JS_CALLEE, etc. macros/inlines. One great part about it is that it asserts that you're using it correctly. The macros/inlines couldn't do this, because they didn't have all the info to do it.

We haven't changed JSNative, but we *have* made it possible in 17 to start using CallArgs even in JSNative as currently defined. Simply create a CallArgs from argc/vp at entry to your native, then use CallArgs for everything, and never use argc/vp. The JS::CallArgsFromVp(argc, vp) method will create a CallArgs for you to use. Here's a JSNative written the old way:

static JSBool
FunctionReturningArgcTimesArg0(JSContext *cx, unsigned argc, JS::Value *vp)
{
// Guard against no arguments or a non-numeric arg0.
if (argc == 0 || !JSVAL_IS_NUMBER(JS_ARGV(cx, vp)[0])) {
JS_SET_RVAL(cx, vp, JS::Int32Value(0));
return true;
}

JS_SET_RVAL(cx, vp, JS::NumberValue(args.length() * args[0].toNumber()));
return true;
}

And here's how you'd write it using CallArgs:

static JSBool
FunctionReturningArgcTimesArg0(JSContext *cx, unsigned argc, JS::Value *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);

// Guard against no arguments or a non-numeric arg0.
if (args.length() == 0 || !args[0].isNumber()) {
args.rval().setInt32(0);
return true;
}

args.rval().set(JS::NumberValue(args.length() * args[0].toNumber()));
return true;
}

Pretty much everything you can do with the inlines/macros, you can do with methods on CallArgs. There is one exception: JS_THIS_OBJECT and JS_ComputeThis still require using |vp|. For these, we're probably going to have people handle the primitive-this-value cases manually. Strict mode functions can do it, so we think it's not generally a burden in most cases.

Where is CallArgs documented, you ask? We're documenting it in the header that defines it. (Given that headers are closer to code, we might follow this tack more in the future, but that's just a guess.) CallArgs is in jsapi.h in 17, mostly undocumented. But on trunk it's in its own js/CallArgs.h header, with copious documentation:

https://hg.mozilla.org/mozilla-central/file/4006800a51db/js/public/CallArgs.h

There are probably small parts of that that will change by the time 24 comes around, but what you see there should be pretty stable, and will correlate with what's in 17. It's worth reading or skimming the entire thing -- I basically relate the entire story of this email there, with a little more detail.

You don't *have* to use CallArgs now. But you probably should start using it now, gradually switching all uses of argc/vp over to it. Then, when JSNative changes, it's just a matter of changing each function's signature, and removing one line from each function, to be compatible with new SpiderMonkey.

== jsval is dead, long live JS::Value ==

In SpiderMonkey 17, jsval is still a C-compatible type, with JS::Value hidden behind C++ #ifdefs. On trunk, JS::Value and jsval are identical types -- with jsval as a typedef of JS::Value. It's likely that we'll eventually remove jsval and have only JS::Value, for simplicity. So you should gradually begin converting uses of the type "jsval" to instead use "JS::Value". Given its pervasive use everywhere, you might consider a |using JS::Value;| at the top of each file to avoid the JS:: prefix everywhere, but that's your own style issue to consider.

That said, the |typedef JS::Value jsval;| that exists now will probably stick around for awhile. There's no particular rush to remove jsval, so feel free to do s/jsval/JS::Value/ at your leisure. And whenever we do end up removing the typedef, you could easily add it to your own code if you preferred that name, to be sure.

Anyway, hope this helps embedders out. We don't have concrete plans for when we'll make the changes noted here, but given the fundamentality of the changes, you probably will want as much lead time as possible to react to them.

Jeff

Anand Rathi

unread,
Mar 26, 2013, 10:40:20 PM3/26/13
to
I am bit suprised by the suggestion to change
typedef JSBool (*JSNative)(JSContext *cx, unsigned argc, jsval *vp);
to two argemnets for performance reason .
Like how many time sone calls JSNative ?? Only at starup or just one time to register the native function ??

But I would welcome any 'more' C++ apis for embeding / extending Spidermonkey in C++ .


My thoughts

Jeff Walden

unread,
Mar 27, 2013, 1:36:23 PM3/27/13
to Anand Rathi
On 03/26/2013 07:40 PM, Anand Rathi wrote:
> I am bit suprised by the suggestion to change
> typedef JSBool (*JSNative)(JSContext *cx, unsigned argc, jsval *vp);
> to two argemnets for performance reason .

To be fair (Nicolas was bugging me about this yesterday :-) ) we don't know the full impact of any perf change. It might be in the noise at times, it might help register allocation inside the JSNative (by the compiler that compiles SpiderMonkey), or it might have other effects. We won't *really* know til we try it.

But the extra assertions it enables are valuable regardless how perf is impacted. Arguably they're a stronger reason to make the change -- and in the short run, before JSNative changes, definitely the stronger one.

> Like how many time sone calls JSNative ?? Only at starup or just one time to register the native function ??

A JSNative is called when the corresponding function gets called, is the general rule. JITs sometimes inline the effect of some JSNative-backed methods when we know exactly how they'll behave because they're standardized methods: String.prototype.charAt is one, Math.max is another. There are many others.

But any JSNative you yourself define will be called every time that function gets called, because SpiderMonkey doesn't know what your JSNative will do when called. So the three-to-two change has the potential to improve every call to your JSNatives.

Jeff

Anand Rathi

unread,
Mar 27, 2013, 8:44:31 PM3/27/13
to Anand Rathi
Oh yes , I mistook it for JS_DefineFunction, its certainly worth optimizing

Nicholas Nethercote

unread,
Mar 27, 2013, 11:18:46 PM3/27/13
to Jeff Walden, dev-tech-...@lists.mozilla.org
On Wed, Mar 27, 2013 at 10:36 AM, Jeff Walden <jwald...@mit.edu> wrote:
> On 03/26/2013 07:40 PM, Anand Rathi wrote:
>> I am bit suprised by the suggestion to change
>> typedef JSBool (*JSNative)(JSContext *cx, unsigned argc, jsval *vp);
>> to two argemnets for performance reason .
>
> To be fair (Nicolas was bugging me about this yesterday :-) ) we don't know the full impact of any perf change. It might be in the noise at times, it might help register allocation inside the JSNative (by the compiler that compiles SpiderMonkey), or it might have other effects. We won't *really* know til we try it.

But the reduction from five to three helped performance, yes?

Nick

Anand Rathi

unread,
Mar 27, 2013, 11:39:28 PM3/27/13
to Jeff Walden, dev-tech-...@lists.mozilla.org
>
> But the reduction from five to three helped performance, yes?
> Nick

Theoretically should have better performance I guess, I am not sure but is it true upto 4 32/64bit parameters get pushed to register than stack?

Boris Zbarsky

unread,
Mar 27, 2013, 11:41:18 PM3/27/13
to
On 3/27/13 11:39 PM, Anand Rathi wrote:
> Theoretically should have better performance I guess, I am not sure but is it true upto 4 32/64bit parameters get pushed to register than stack?

It depends on the architecture.

-Boris

Anand Rathi

unread,
Mar 27, 2013, 11:56:45 PM3/27/13
to Jeff Walden, dev-tech-...@lists.mozilla.org
One more question

JS::CallArgs args = JS::CallArgsFromVp(argc, vp);

Having this in the code will is like pushing additional stuff in stack so its like moving around responsibility .

So reducing parameter will only benefit empty JSNative ?

Anand Rathi

unread,
Mar 27, 2013, 11:57:00 PM3/27/13
to

Jeff Walden

unread,
Mar 28, 2013, 3:55:10 AM3/28/13
to
Not necessarily. Modern C++ compilers can recognize class instances used on the stack, whose addresses (and sub-addresses of components) do not escape, and effectively split them up into their constituent components. Thus instead of a stacked class, you might just have args.argc_ in %rsi and args.argv_ in %rdx on x86-64, these being the registers used to pass the second and third arguments there. So the CallArgs (and the steps to construct it) would boil away to nothing in optimized builds, in the current (cx, argc, vp) JSNative typedef.

(In debug builds CallArgs has a couple other values to implement those proper-usage assertions, and the stacking cost might not boil away. But debug builds exist so you can test them, not so you can ship them. So as long as having to stack up a CallArgs, or perform any operations upon it, doesn't ding perf by orders of magnitude -- and none of this does -- it doesn't matter there.)

Jeff

Blake Kaplan

unread,
Apr 2, 2013, 5:18:03 PM4/2/13
to
Nicholas Nethercote <n.neth...@gmail.com> wrote:
> But the reduction from five to three helped performance, yes?

We don't have good numbers on that because the move from five to three also
came with the benefit of not having to push a JSStackFrame to do the call.
Therefore, any benefit gained from having fewer arguments was way overshadowed
by no longer having to create and populate a gigantic struct (which was in
fact a large performance win).
--
Blake Kaplan
0 new messages