From 43a6d946f81b74988ced85973dd7c418704fbaf5 Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Thu, 23 Apr 2026 03:31:22 -0400 Subject: [PATCH] Issue #3049 - Support more LoongArch minimal JIT branch ops --- .../tests/basic/loongarch-minimal-jit.js | 79 ++++++++++++++ js/src/jit/LoongArchMinimalJit.cpp | 101 ++++++++++++++++++ 2 files changed, 180 insertions(+) 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 5b07c7bb93..15eb897ea3 100644 --- a/js/src/jit-test/tests/basic/loongarch-minimal-jit.js +++ b/js/src/jit-test/tests/basic/loongarch-minimal-jit.js @@ -192,6 +192,70 @@ function sumRange(n) { return total; } +function coalesceArg(a) { + return a ?? 9; +} + +function coalesceLocal() { + var x; + return x ?? 5; +} + +function coalesceNull() { + return null ?? 5; +} + +function sparseSwitchValue(a) { + var out = -1; + switch (a) { + case 1: + out = 10; + break; + case 4: + out = 40; + break; + default: + out = 99; + break; + } + return out; +} + +function denseSwitchValue(a) { + var out = -1; + switch (a) { + case 0: + out = 7; + break; + case 1: + out = 8; + break; + case 2: + out = 9; + break; + default: + out = 11; + break; + } + return out; +} + +function groupedSwitchValue(a) { + var out = 0; + switch (a) { + case 0: + case 1: + out = 10; + break; + case 3: + out = 30; + default: + out = out + 1; + break; + } + return out; +} + for (var i = 0; i < 100; i++) { assertEq(add(i, i + 1), (i + i + 1)); assertEq(withLocal(i, 7), i + 7); @@ -272,6 +336,21 @@ assertEq(chooseOrder(2, 3), true); assertEq(chooseOrder(3, 2), false); assertEq(sumRange(0), 0); assertEq(sumRange(5), 10); +assertEq(coalesceArg(0), 0); +assertEq(coalesceArg(7), 7); +assertEq(coalesceLocal(), 5); +assertEq(coalesceNull(), 5); +assertEq(sparseSwitchValue(1), 10); +assertEq(sparseSwitchValue(4), 40); +assertEq(sparseSwitchValue(2), 99); +assertEq(denseSwitchValue(0), 7); +assertEq(denseSwitchValue(1), 8); +assertEq(denseSwitchValue(2), 9); +assertEq(denseSwitchValue(5), 11); +assertEq(groupedSwitchValue(0), 10); +assertEq(groupedSwitchValue(1), 10); +assertEq(groupedSwitchValue(3), 31); +assertEq(groupedSwitchValue(5), 1); (function testPropertyWrappers() { function getX(obj) { return obj.x; } diff --git a/js/src/jit/LoongArchMinimalJit.cpp b/js/src/jit/LoongArchMinimalJit.cpp index 98975d6c14..9276869b41 100644 --- a/js/src/jit/LoongArchMinimalJit.cpp +++ b/js/src/jit/LoongArchMinimalJit.cpp @@ -854,7 +854,22 @@ class MinimalLoongArchCompiler case JSOP_LOOPENTRY: case JSOP_JUMPTARGET: case JSOP_NOP: + case JSOP_CONDSWITCH: break; + case JSOP_COALESCE: { + if (!stackDepth_) + return false; + MinimalValueKind kind = stack_[stackDepth_ - 1].kind; + if (kind == MinimalValueKind::Null || kind == MinimalValueKind::Undefined) + break; + 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_OR: case JSOP_AND: { if (!stackDepth_) @@ -867,6 +882,87 @@ class MinimalLoongArchCompiler return false; break; } + case JSOP_CASE: { + StackValue rhs; + StackValue lhs; + if (!pop(&rhs) || !pop(&lhs)) + return false; + uint32_t target = uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc)); + if (lhs.kind != rhs.kind) { + stack_[stackDepth_++] = lhs; + break; + } + if (!recordTargetState(target)) + return false; + if (lhs.kind == MinimalValueKind::Null || lhs.kind == MinimalValueKind::Undefined) { + if (!emitBranch(BranchKind::Always, zero, target)) + return false; + reachable_ = false; + break; + } + if (!emitEq(lhs.reg, rhs.reg, WideScratch, false)) + return false; + if (!emitBranch(BranchKind::IfTrue, WideScratch, target)) + return false; + stack_[stackDepth_++] = lhs; + break; + } + case JSOP_TABLESWITCH: { + StackValue input; + if (!pop(&input)) + return false; + if (input.kind != MinimalValueKind::Int32) + return false; + + uint32_t defaultTarget = uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc)); + if (!recordTargetState(defaultTarget)) + return false; + + jsbytecode* tablePc = pc + JUMP_OFFSET_LEN; + int32_t low = GET_JUMP_OFFSET(tablePc); + tablePc += JUMP_OFFSET_LEN; + int32_t high = GET_JUMP_OFFSET(tablePc); + tablePc += JUMP_OFFSET_LEN; + if (high < low) + return false; + + if (!emitLoadImm32(NarrowScratch, low)) + return false; + if (!emitCompare(JSOP_LT, input.reg, NarrowScratch, WideScratch)) + return false; + if (!emitBranch(BranchKind::IfTrue, WideScratch, defaultTarget)) + return false; + + if (!emitLoadImm32(NarrowScratch, high)) + return false; + if (!emitCompare(JSOP_GT, input.reg, NarrowScratch, WideScratch)) + return false; + if (!emitBranch(BranchKind::IfTrue, WideScratch, defaultTarget)) + return false; + + uint32_t caseCount = uint32_t(high - low + 1); + for (uint32_t i = 0; i < caseCount; i++) { + int32_t rel = GET_JUMP_OFFSET(tablePc); + tablePc += JUMP_OFFSET_LEN; + if (!rel) + continue; + + uint32_t target = uint32_t(int32_t(pcOffset) + rel); + if (!recordTargetState(target)) + return false; + if (!emitLoadImm32(NarrowScratch, low + int32_t(i))) + return false; + if (!emitEq(input.reg, NarrowScratch, WideScratch, false)) + return false; + if (!emitBranch(BranchKind::IfTrue, WideScratch, target)) + return false; + } + + if (!emitBranch(BranchKind::Always, zero, defaultTarget)) + return false; + reachable_ = false; + break; + } case JSOP_ADD: case JSOP_SUB: case JSOP_MUL: @@ -1031,7 +1127,12 @@ class MinimalLoongArchCompiler return false; stack_[stackDepth_ - 1].kind = MinimalValueKind::Int32; break; + case JSOP_DEFAULT: case JSOP_GOTO: { + if (!stackDepth_ && op == JSOP_DEFAULT) + return false; + if (op == JSOP_DEFAULT) + stackDepth_--; uint32_t target = uint32_t(int32_t(pcOffset) + GET_JUMP_OFFSET(pc)); if (!recordTargetState(target)) return false;