diff --git a/js/src/jit-test/tests/basic/loongarch-minimal-jit.js b/js/src/jit-test/tests/basic/loongarch-minimal-jit.js index ef5009b423..9c307ef9df 100644 --- a/js/src/jit-test/tests/basic/loongarch-minimal-jit.js +++ b/js/src/jit-test/tests/basic/loongarch-minimal-jit.js @@ -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); +})(); diff --git a/js/src/jit/LoongArchMinimalJit.cpp b/js/src/jit/LoongArchMinimalJit.cpp index 2e1df0f049..1b5e84f173 100644 --- a/js/src/jit/LoongArchMinimalJit.cpp +++ b/js/src/jit/LoongArchMinimalJit.cpp @@ -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()) + return true; + + jsbytecode* pc = script->code() + script->loongArchMinimalFastPathOffset(); + RootedPropertyName name(cx, script->getName(pc)); + RootedShape shape(cx, obj->as().lookupPure(name)); + if (!shape || !shape->hasSlot()) + return true; + + if (kind == MinimalFastPathGetter) { + if (!shape->hasDefaultGetter()) + return true; + + Value result = obj->as().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().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; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 7b662df171..4d53a000d7 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -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_ &&