Issue #3049 - Track LoongArch minimal JIT control flow state

This commit is contained in:
Basilisk-Dev 2026-04-23 03:26:35 -04:00 committed by wuggy
commit 460765fc80
2 changed files with 164 additions and 10 deletions

View file

@ -158,6 +158,40 @@ function conditional(a, b) {
return false;
}
function logicalOr(a, b) {
return a || b;
}
function logicalAnd(a, b) {
return a && b;
}
function logicalBoolOr(a, b) {
return (a < b) || (b < a);
}
function logicalBoolAnd(a, b) {
return (a < b) && (b < 10);
}
function minValue(a, b) {
return a < b ? a : b;
}
function chooseOrder(a, b) {
return a < b ? true : false;
}
function sumRange(n) {
var i = 0;
var total = 0;
while (i < n) {
total = total + i;
i = i + 1;
}
return total;
}
for (var i = 0; i < 100; i++) {
assertEq(add(i, i + 1), (i + i + 1));
assertEq(withLocal(i, 7), i + 7);
@ -224,6 +258,20 @@ assertEq(unsetLocal(), undefined);
assertEq(unsetLooseEqNull(), true);
assertEq(conditional(2, 3), true);
assertEq(conditional(3, 2), false);
assertEq(logicalOr(0, 7), 7);
assertEq(logicalOr(5, 7), 5);
assertEq(logicalAnd(0, 7), 0);
assertEq(logicalAnd(5, 7), 7);
assertEq(logicalBoolOr(2, 3), true);
assertEq(logicalBoolOr(3, 3), false);
assertEq(logicalBoolAnd(2, 3), true);
assertEq(logicalBoolAnd(3, 2), false);
assertEq(minValue(2, 3), 2);
assertEq(minValue(5, 1), 1);
assertEq(chooseOrder(2, 3), true);
assertEq(chooseOrder(3, 2), false);
assertEq(sumRange(0), 0);
assertEq(sumRange(5), 10);
(function testPropertyWrappers() {
function getX(obj) { return obj.x; }

View file

@ -179,6 +179,21 @@ struct BranchPatch
LoongArchReg reg;
};
struct ControlFlowState
{
bool initialized;
size_t stackDepth;
LoongArchReg stackRegs[mozilla::ArrayLength(StackRegs)];
MinimalValueKind stackKinds[mozilla::ArrayLength(StackRegs)];
MinimalValueKind argKinds[mozilla::ArrayLength(ArgRegs)];
MinimalValueKind localKinds[mozilla::ArrayLength(LocalRegs)];
ControlFlowState()
: initialized(false),
stackDepth(0)
{ }
};
class MinimalLoongArchCompiler
{
struct StackValue
@ -196,8 +211,10 @@ class MinimalLoongArchCompiler
MinimalValueKind argKinds_[mozilla::ArrayLength(ArgRegs)];
MinimalValueKind localKinds_[mozilla::ArrayLength(LocalRegs)];
size_t stackDepth_;
bool reachable_;
bool sawReturn_;
Vector<int32_t, 0, SystemAllocPolicy> pcToWord_;
Vector<ControlFlowState, 0, SystemAllocPolicy> pcStates_;
Vector<BranchPatch, 0, SystemAllocPolicy> branchPatches_;
bool emit(uint32_t word) {
@ -207,11 +224,78 @@ class MinimalLoongArchCompiler
return true;
}
bool restoreState(const ControlFlowState& state) {
stackDepth_ = state.stackDepth;
for (size_t i = 0; i < stackDepth_; i++) {
stack_[i].reg = state.stackRegs[i];
stack_[i].kind = state.stackKinds[i];
}
for (size_t i = 0; i < mozilla::ArrayLength(argKinds_); i++)
argKinds_[i] = state.argKinds[i];
for (size_t i = 0; i < mozilla::ArrayLength(localKinds_); i++)
localKinds_[i] = state.localKinds[i];
return true;
}
bool stateMatches(const ControlFlowState& state) const {
if (stackDepth_ != state.stackDepth)
return false;
for (size_t i = 0; i < stackDepth_; i++) {
if (stack_[i].reg != state.stackRegs[i] ||
stack_[i].kind != state.stackKinds[i])
{
return false;
}
}
for (size_t i = 0; i < mozilla::ArrayLength(argKinds_); i++) {
if (argKinds_[i] != state.argKinds[i])
return false;
}
for (size_t i = 0; i < mozilla::ArrayLength(localKinds_); i++) {
if (localKinds_[i] != state.localKinds[i])
return false;
}
return true;
}
bool captureState(ControlFlowState* state) const {
state->initialized = true;
state->stackDepth = stackDepth_;
for (size_t i = 0; i < stackDepth_; i++) {
state->stackRegs[i] = stack_[i].reg;
state->stackKinds[i] = stack_[i].kind;
}
for (size_t i = 0; i < mozilla::ArrayLength(argKinds_); i++)
state->argKinds[i] = argKinds_[i];
for (size_t i = 0; i < mozilla::ArrayLength(localKinds_); i++)
state->localKinds[i] = localKinds_[i];
return true;
}
bool recordTargetState(uint32_t targetPcOffset) {
if (targetPcOffset >= pcStates_.length())
return false;
ControlFlowState& state = pcStates_[targetPcOffset];
if (state.initialized)
return stateMatches(state);
return captureState(&state);
}
bool markBytecode(jsbytecode* pc) {
uint32_t offset = uint32_t(pc - script_->code());
MOZ_ASSERT(offset < pcToWord_.length());
pcToWord_[offset] = int32_t(wordCount_);
return true;
ControlFlowState& state = pcStates_[offset];
if (!state.initialized)
return true;
if (reachable_)
return stateMatches(state);
reachable_ = true;
return restoreState(state);
}
bool emitMove(LoongArchReg dst, LoongArchReg src) {
@ -614,6 +698,7 @@ class MinimalLoongArchCompiler
wordCount_(0),
failPatchCount_(0),
stackDepth_(0),
reachable_(true),
sawReturn_(false)
{
for (size_t i = 0; i < mozilla::ArrayLength(argKinds_); i++)
@ -628,6 +713,8 @@ class MinimalLoongArchCompiler
if (!pcToWord_.appendN(-1, script_->length() + 1))
return false;
if (!pcStates_.appendN(ControlFlowState(), script_->length() + 1))
return false;
for (size_t i = 0; i < script_->nfixed(); i++) {
if (!emitLoadImm32(LocalRegs[i], 0))
@ -640,6 +727,11 @@ class MinimalLoongArchCompiler
return false;
uint32_t pcOffset = uint32_t(pc - script_->code());
if (!reachable_) {
pc += GetBytecodeLength(pc);
continue;
}
JSOp op = JSOp(*pc);
switch (op) {
case JSOP_GETARG: {
@ -763,6 +855,18 @@ class MinimalLoongArchCompiler
case JSOP_JUMPTARGET:
case JSOP_NOP:
break;
case JSOP_OR:
case JSOP_AND: {
if (!stackDepth_)
return false;
uint32_t target = uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc));
if (!recordTargetState(target))
return false;
BranchKind kind = (op == JSOP_OR) ? BranchKind::IfTrue : BranchKind::IfFalse;
if (!emitBranch(kind, stack_[stackDepth_ - 1].reg, target))
return false;
break;
}
case JSOP_ADD:
case JSOP_SUB:
case JSOP_MUL:
@ -927,24 +1031,26 @@ class MinimalLoongArchCompiler
return false;
stack_[stackDepth_ - 1].kind = MinimalValueKind::Int32;
break;
case JSOP_GOTO:
if (!emitBranch(BranchKind::Always, zero,
uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc))))
{
case JSOP_GOTO: {
uint32_t target = uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc));
if (!recordTargetState(target))
return false;
}
if (!emitBranch(BranchKind::Always, zero, target))
return false;
reachable_ = false;
break;
}
case JSOP_IFEQ:
case JSOP_IFNE: {
StackValue cond;
if (!pop(&cond))
return false;
BranchKind kind = (op == JSOP_IFNE) ? BranchKind::IfTrue : BranchKind::IfFalse;
if (!emitBranch(kind, cond.reg,
uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc))))
{
uint32_t target = uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc));
if (!recordTargetState(target))
return false;
BranchKind kind = (op == JSOP_IFNE) ? BranchKind::IfTrue : BranchKind::IfFalse;
if (!emitBranch(kind, cond.reg, target))
return false;
}
break;
}
case JSOP_RETURN: