Issue #3049 - Add property-wrapper fast path to minimal LoongArch JIT

This commit is contained in:
Basilisk-Dev 2026-04-16 11:36:57 -04:00 committed by wuggy
commit 1466d0a2fa
3 changed files with 174 additions and 1 deletions

View file

@ -23,3 +23,13 @@ for (var i = 0; i < 100; i++) {
assertEq(overflow(2147483647, 1), 2147483648);
assertEq(unsupported(2, 3), 5.5);
(function testPropertyWrappers() {
function getX(obj) { return obj.x; }
function setX(obj, value) { return obj.x = value; }
var obj = { x: 7 };
assertEq(getX(obj), 7);
assertEq(setX(obj, 42), 42);
assertEq(obj.x, 42);
})();

View file

@ -15,6 +15,7 @@
#include "jsopcode.h"
#include "jsscript.h"
#include "vm/Interpreter.h"
#include "vm/NativeObject-inl.h"
#include "vm/Stack.h"
using namespace js;
@ -55,6 +56,13 @@ static constexpr LoongArchReg NarrowScratch = t8;
using TinyLoongArchJitCode = bool (*)(int32_t arg0, int32_t arg1, int32_t* out);
enum MinimalFastPathKind : uint8_t {
MinimalFastPathUninitialized = 0,
MinimalFastPathNone = 1,
MinimalFastPathGetter = 2,
MinimalFastPathSetter = 3
};
static inline uint32_t
EncodeThreeReg(uint32_t opcode, LoongArchReg rd, LoongArchReg rj, LoongArchReg rk)
{
@ -563,6 +571,124 @@ CanDirectCallMinimalJit(JSContext* cx, HandleFunction fun, JSScript* script, con
return true;
}
static bool
PreparePropertyFastPath(JSScript* script)
{
if (script->loongArchMinimalFastPathKind() != MinimalFastPathUninitialized)
return script->loongArchMinimalFastPathKind() != MinimalFastPathNone;
uint8_t objectArg = 0;
uint8_t valueArg = 0;
uint32_t propOffset = 0;
enum State { Start, SawObject, SawValue, SawProp } state = Start;
for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
switch (JSOp(*pc)) {
case JSOP_JUMPTARGET:
case JSOP_NOP:
continue;
case JSOP_GETARG: {
uint32_t arg = GET_ARGNO(pc);
if (state == Start) {
objectArg = uint8_t(arg);
state = SawObject;
continue;
}
if (state == SawObject) {
valueArg = uint8_t(arg);
state = SawValue;
continue;
}
script->setLoongArchMinimalFastPath(MinimalFastPathNone, 0, 0, 0);
return false;
}
case JSOP_GETPROP:
if (state == SawObject) {
propOffset = uint32_t(pc - script->code());
state = SawProp;
continue;
}
break;
case JSOP_SETPROP:
case JSOP_STRICTSETPROP:
if (state == SawValue) {
propOffset = uint32_t(pc - script->code());
script->setLoongArchMinimalFastPath(MinimalFastPathSetter, objectArg, valueArg, propOffset);
state = SawProp;
continue;
}
break;
case JSOP_RETURN:
if (state == SawProp) {
uint8_t kind = script->loongArchMinimalFastPathKind();
if (kind == MinimalFastPathSetter)
return true;
script->setLoongArchMinimalFastPath(MinimalFastPathGetter, objectArg, 0, propOffset);
return true;
}
break;
default:
break;
}
script->setLoongArchMinimalFastPath(MinimalFastPathNone, 0, 0, 0);
return false;
}
script->setLoongArchMinimalFastPath(MinimalFastPathNone, 0, 0, 0);
return false;
}
static bool
TryFastPropertyPath(JSContext* cx, JSScript* script, const CallArgs& args, bool* handled)
{
*handled = false;
if (!PreparePropertyFastPath(script))
return true;
uint8_t kind = script->loongArchMinimalFastPathKind();
uint8_t objectArg = script->loongArchMinimalFastPathObjectArg();
if (args.length() <= objectArg || !args[objectArg].isObject())
return true;
RootedObject obj(cx, &args[objectArg].toObject());
if (!obj->is<NativeObject>())
return true;
jsbytecode* pc = script->code() + script->loongArchMinimalFastPathOffset();
RootedPropertyName name(cx, script->getName(pc));
RootedShape shape(cx, obj->as<NativeObject>().lookupPure(name));
if (!shape || !shape->hasSlot())
return true;
if (kind == MinimalFastPathGetter) {
if (!shape->hasDefaultGetter())
return true;
Value result = obj->as<NativeObject>().getSlot(shape->slot());
if (result.isMagic())
return true;
args.rval().set(result);
*handled = true;
return true;
}
if (kind == MinimalFastPathSetter) {
uint8_t valueArg = script->loongArchMinimalFastPathValueArg();
if (args.length() <= valueArg || !shape->hasDefaultSetter() || !shape->writable())
return true;
obj->as<NativeObject>().setSlotWithType(cx, shape, args[valueArg]);
args.rval().set(args[valueArg]);
*handled = true;
return true;
}
return true;
}
static bool
CanUseMinimalJit(RunState& state, InvokeState& invoke)
{
@ -606,6 +732,12 @@ TryCallLoongArchMinimalJit(JSContext* cx, HandleFunction fun, const CallArgs& ar
*handled = false;
JSScript* script = fun ? fun->nonLazyScript() : nullptr;
if (!script)
return true;
if (!TryFastPropertyPath(cx, script, args, handled) || *handled)
return !cx->isExceptionPending();
if (!CanDirectCallMinimalJit(cx, fun, script, args))
return true;
@ -631,6 +763,14 @@ TryEnterLoongArchMinimalJit(JSContext* cx, RunState& state)
return false;
InvokeState& invoke = *state.asInvoke();
bool handled = false;
if (!TryFastPropertyPath(cx, state.script(), invoke.args(), &handled))
return false;
if (handled) {
state.setReturnValue(invoke.args().rval());
return true;
}
if (!CanUseMinimalJit(state, invoke))
return false;

View file

@ -923,10 +923,15 @@ class JSScript : public js::gc::TenuredCell
uint8_t* baselineOrIonSkipArgCheck;
uint8_t* loongArchMinimalJitCode_;
uint8_t loongArchMinimalFastPathKind_;
uint8_t loongArchMinimalFastPathObjectArg_;
uint8_t loongArchMinimalFastPathValueArg_;
uint8_t loongArchMinimalFastPathReserved_;
// 32-bit fields.
uint32_t loongArchMinimalJitCodeSize_;
uint32_t loongArchMinimalFastPathOffset_;
uint32_t dataSize_; /* size of the used part of the data array */
@ -1605,6 +1610,24 @@ class JSScript : public js::gc::TenuredCell
loongArchMinimalJitCode_ = code;
loongArchMinimalJitCodeSize_ = size;
}
uint8_t loongArchMinimalFastPathKind() const {
return loongArchMinimalFastPathKind_;
}
uint8_t loongArchMinimalFastPathObjectArg() const {
return loongArchMinimalFastPathObjectArg_;
}
uint8_t loongArchMinimalFastPathValueArg() const {
return loongArchMinimalFastPathValueArg_;
}
uint32_t loongArchMinimalFastPathOffset() const {
return loongArchMinimalFastPathOffset_;
}
void setLoongArchMinimalFastPath(uint8_t kind, uint8_t objectArg, uint8_t valueArg, uint32_t offset) {
loongArchMinimalFastPathKind_ = kind;
loongArchMinimalFastPathObjectArg_ = objectArg;
loongArchMinimalFastPathValueArg_ = valueArg;
loongArchMinimalFastPathOffset_ = offset;
}
bool isRelazifiable() const {
return (selfHosted() || lazyScript) && !hasInnerFunctions_ && !types_ &&