Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2a4fec7
[JSC][armv7] Fix BBQJIT::F64CopySign on armv7
eugeneia Feb 27, 2025
c118d36
[ARMv7] Build-webkit should support --32-bit on ARM64
justinmichaud Feb 27, 2025
bfe0311
Handle wide Air::Arg offsets
aoikonomopoulos Feb 26, 2025
dcba947
[JSC] Fix integer overflow on armv7 in CompleteSubspace::tryAllocateSlow
eugeneia Mar 10, 2025
9a4ac50
[ARMv7] Skip branch compaction when diff would cause misalignment
justinmichaud May 27, 2025
a7c87b8
[JSC] Fix ARMv7 segfault in BBQJIT by avoid the use of `wasmScratchGPR`
sosukesuzuki Jul 9, 2025
87bb90f
Add additional padding for patchable jumps and calls on armv7
justinmichaud Jul 14, 2025
34c549e
[ARMv7] Add padding to align patchable calls
justinmichaud Jul 21, 2025
35cd9f9
[JSC] Fix shiftI64 spilling only on one side of a branch on 32-bit
mikhailramalho Aug 29, 2025
fb005b7
Thumb mode not detected on gcc for arm
efecanicoz-atlas Oct 7, 2025
441c595
[JSC][32-bit] Fix missing move when shift == 0 in BBQ
mikhailramalho Oct 14, 2025
07ec4bb
[JSC][32-bit] Fix BBQ's I64Or
mikhailramalho Nov 11, 2025
8f11ea1
[JSC][ARMv7] Don't issue a ldrd when the register holding the address…
mikhailramalho Nov 14, 2025
3def3bd
[32-bit] Armv7 tail call shuffler should not run out of registers whe…
justinmichaud Jan 14, 2026
d021461
CodeBlock should reset its StubInfo when jettisoned
danlliu Apr 4, 2025
293d6f0
LLInt GetByIdModeMetadata should not hold potentially dead structure IDs
danlliu Apr 4, 2025
90450c5
[JSC] Eagerly unlinking CodeBlock when jettisoning
Constellation Jun 13, 2025
c75f65a
Exclude non-user portions of the main thread stack from stack scannin…
justinmichaud May 29, 2025
32934c9
[JSC] GetByIdModeMetadata's mode needs to be set when not using 64bit…
Constellation Apr 7, 2025
46f4ee0
[JSC][armv7] Verify MacroAssemblerARMv7::branch32 usage in debug builds
eugeneia Mar 24, 2025
9fbbbaf
[ARMv7] Set temporaryCallFrame in WebAssembly.asm
aoikonomopoulos Jul 31, 2025
caf5df2
Js/Wasm engine produce an invalid ref.cast runtime fail for func.ref …
Constellation Jun 6, 2025
75bc41b
Debug build fix
justinmichaud Aug 5, 2026
b5fd112
[JSC] Fix an !binding.isScratch() assertion failure on 32-bit
mikhailramalho Sep 11, 2025
febb62e
[JSC] Improve shift operations in 32-bit BBQ
mikhailramalho Nov 11, 2025
7da99d4
[JSC][32-bit] Improve BBQ's load/store operations for ARMv7
mikhailramalho Nov 13, 2025
9e6cd35
[JSC][32-bit] Enable fused branch compare on BBQ for 32-bit platforms
mikhailramalho Nov 19, 2025
527b1bc
[ARMv7] Avoid unaligned strd
justinmichaud Jul 31, 2025
6a78975
Skip some JSTests on $memoryLimited
aoikonomopoulos Feb 26, 2025
d32b8f3
arrayInitElem should check if the segment is null
danlliu Feb 14, 2025
5e12b7b
Do not issue unaligned ldrd
justinmichaud Aug 7, 2026
abe4fa3
Fix BBQ compare below 0
justinmichaud Aug 7, 2026
86dddb9
[ARMv7] Fix dfg clobbers with dataIC
justinmichaud Aug 7, 2026
0beae43
[ARMv7] MASM and Assembler fixes, asserts, and an absurd number of ge…
justinmichaud Aug 8, 2026
97b4a9e
ASSERTION FAILED: constructor.isObject() when OSR from an inlined fun…
hyjorc1 Oct 5, 2025
7fc6f31
Build fixes
justinmichaud Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions JSTests/stress/instanceof-osr-exit-hasInstance-getter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function F1() {
Object instanceof Proxy;
}
noInline(F1);

let count = 0;
function f20() {
count++;
OSRExit();
return () => { };
}
Object.defineProperty(Proxy, Symbol.hasInstance, { get: f20 });

globalThis.testLoopCount ??= 1e4;
for (let i = 0; i < testLoopCount; i++) {
F1();
}
if (count != testLoopCount)
throw new Error("bad!");
19 changes: 19 additions & 0 deletions JSTests/stress/instanceof-osr-exit-prototype-getter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function F1() {
Object instanceof Proxy;
}
noInline(F1);

let count = 0;
function f20() {
count++;
OSRExit();
return f20;
}
Object.defineProperty(Proxy, "prototype", { get: f20 });

globalThis.testLoopCount ??= 1e4;
for (let i = 0; i < testLoopCount; i++) {
F1();
}
if (count != testLoopCount)
throw new Error("bad!");
88 changes: 88 additions & 0 deletions JSTests/stress/proxy-ic-does-not-clobber-callee-saves.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// A proxy inline cache emits a JS call, and values the enclosing JIT holds in callee save registers
// have to survive it. Each function below keeps more live integers than ARMv7 has allocatable GPRs,
// so some of them land in the registers that hold metadataTable and jitData there.

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error(`bad value: ${actual}, expected ${expected}`);
}

const handler = {
get(target, property) {
return target[property];
},
set(target, property, value) {
target[property] = value;
return true;
},
};

const loadProxy = new Proxy({ field: 42 }, handler);
const indexedProxy = new Proxy({ 0: 7 }, handler);
const storeProxy = new Proxy({ field: 0 }, handler);

function load(proxy, x) {
const v0 = (x + 1) | 0;
const v1 = (x + 2) | 0;
const v2 = (x + 3) | 0;
const v3 = (x + 4) | 0;
const v4 = (x + 5) | 0;
const v5 = (x + 6) | 0;
const v6 = (x + 7) | 0;
const v7 = (x + 8) | 0;
const v8 = (x + 9) | 0;
const v9 = (x + 10) | 0;
const v10 = (x + 11) | 0;
const v11 = (x + 12) | 0;
const got = proxy.field;
return (v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + got) | 0;
}
noInline(load);

function loadByVal(proxy, x, index) {
const v0 = (x + 1) | 0;
const v1 = (x + 2) | 0;
const v2 = (x + 3) | 0;
const v3 = (x + 4) | 0;
const v4 = (x + 5) | 0;
const v5 = (x + 6) | 0;
const v6 = (x + 7) | 0;
const v7 = (x + 8) | 0;
const v8 = (x + 9) | 0;
const v9 = (x + 10) | 0;
const v10 = (x + 11) | 0;
const v11 = (x + 12) | 0;
const got = proxy[index];
return (v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + got) | 0;
}
noInline(loadByVal);

function store(proxy, x) {
const v0 = (x + 1) | 0;
const v1 = (x + 2) | 0;
const v2 = (x + 3) | 0;
const v3 = (x + 4) | 0;
const v4 = (x + 5) | 0;
const v5 = (x + 6) | 0;
const v6 = (x + 7) | 0;
const v7 = (x + 8) | 0;
const v8 = (x + 9) | 0;
const v9 = (x + 10) | 0;
const v10 = (x + 11) | 0;
const v11 = (x + 12) | 0;
proxy.field = x;
return (v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11) | 0;
}
noInline(store);

// The first call runs before any inline cache exists, so its result is the oracle for every tier.
const expectedLoad = load(loadProxy, 1);
const expectedLoadByVal = loadByVal(indexedProxy, 1, 0);
const expectedStore = store(storeProxy, 1);

for (let i = 0; i < 5e4; ++i) {
shouldBe(load(loadProxy, 1), expectedLoad);
shouldBe(loadByVal(indexedProxy, 1, 0), expectedLoadByVal);
shouldBe(store(storeProxy, 1), expectedStore);
shouldBe(storeProxy.field, 1);
}
137 changes: 137 additions & 0 deletions JSTests/stress/tail-call-register-pressure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use strict";

function getModuleExports() {
return () => {}
};

function ownKeys(ee, te) {
var re = Object.keys(ee);
if (Object.getOwnPropertySymbols) {
var ne = Object.getOwnPropertySymbols(ee);
te && (ne = ne.filter((function(te) {
return Object.getOwnPropertyDescriptor(ee, te).enumerable
}))),
re.push.apply(re, ne)
}
return re
}

function _objectSpread(ee) {
for (var te = 1; te < arguments.length; te++) {
var re = null != arguments[te] ? arguments[te] : {};
te % 2 ? ownKeys(Object(re), !0).forEach((function(te) {
getModuleExports()(ee, te, re[te])
})) : Object.getOwnPropertyDescriptors ? Object.defineProperties(ee, Object.getOwnPropertyDescriptors(re)) : ownKeys(Object(re)).forEach((function(te) {
Object.defineProperty(ee, te, Object.getOwnPropertyDescriptor(re, te))
}))
}
return ee
}

var ae = {
cleanAndTrimSelfLink: function() {
}
};

var browseItems = function(ee, te, re) {
function parseBrowseItem(ee, te, re, ne, ie) {
var ce, le, de, he;
if (ee.hasEmbedded("linearInfo")) {
var fe = ee.getEmbedded("linearInfo"),
pe = Object(ae.cleanSelfLink)(fe.getFirstAction("channel").getRawActionUrl());
if (!ne[pe] && !ie) return;
}

var ge = function parseBrowseItemUrl(ee, te) {
var re = ee.getProp("entityId"),
ne = ee.getProp("entityType"),
ie = ee.getProp("seriesProgramId"),
oe = ee.getProp("_type");
return "entity/".concat(re)
}(ee, he),
me = ee.getProps();
if (("NETWORK" !== te || ee.hasEmbedded("contentProvider")) && ("3X4_PROGRAM_LINEAR" !== te || ee.hasEmbedded("linearInfo"))) {
var ve = ee.hasAction("programImageLink") ? ee.getFirstAction("programImageLink") : re.programImageLink,
Se = ee.hasAction("programFallbackImageLink"),
_t = Se ? ee.getFirstAction("programFallbackImageLink") : re.programFallbackImageLink;

var Et, kt, Tt, Pt = _t.setParams(de).getActionUrl();
return Et = Se ? Pt : ve.setParams(de).getActionUrl(), me.contentRating && me.contentRatingScheme && (kt = {
scheme: me.contentRatingScheme,
name: me.contentRating
}), (null === (ce = me.ordering) || void 0 === ce ? void 0 : ce.criticScore) && (null === (le = me.ordering) || void 0 === le ? void 0 : le.fanScore) && (Tt = {
rt: {
criticScore: Math.floor(me.ordering.criticScore),
criticCertified: Math.floor(me.ordering.criticScore) >= 75,
criticRotten: Math.floor(me.ordering.criticScore) < 60,
fanScore: Math.floor(me.ordering.fanScore),
fanRotten: Math.floor(me.ordering.fanScore) < 60
}
}), _objectSpread({
_type: me._type
}, me.adBrand && {
adBrand: me.adBrand
}, {}, me.entityId && {
entityId: me.entityId
}, {}, me.entityType && {
entityType: me.entityType
}, {}, me.episodeNumber && {
episodeNumber: me.episodeNumber
}, {}, he && {
linearInfo: he
}, {}, me.numberOfEpisodes && {
numberOfEpisodes: me.numberOfEpisodes
}, {}, me.programType && {
programType: me.programType
}, {}, kt && {
rating: kt
}, {}, Tt && {
reviews: Tt
}, {}, me.hasDVS && {
dvs: me.hasDVS
}, {}, me.isHD && {
hd: me.isHD
}, {}, me.closedCaption && {
cc: me.closedCaption
}, {}, me.isSAP && {
sap: me.isSAP
}, {}, me.releaseYear && {
releaseYear: me.releaseYear
}, {}, me.seasonNumber && {
seasonNumber: me.seasonNumber
}, {}, me.seriesTitle && {
seriesTitle: me.seriesTitle
}, {}, {
tileRenderStyle: me.tileRenderStyle || te,
title: me.title,
subtitle: me.subtitle,
image: Et,
fallbackImage: Pt,
url: ge
})
}
}

return parseBrowseItem(ee, te, re,{},{})
}

const runTest = function() {
for (var i = 1; i < 100; ++i) {
browseItems(
{
hasAction: () => {},
hasEmbedded: (i) => { return i === "contentProvider"; },
getProp: () => { return undefined },
getProps: () => { return {} },
getFirstAction: () => { return { getRawActionUrl: () => "url" } },
},
{},
{
programImageLink: { setParams: () => { return { getActionUrl: () => "programImageLink" }; } },
programFallbackImageLink: { setParams: () => { return { getActionUrl: () => "programFallbackImageLinkrl" }; } },
}
)
}
}

runTest()
74 changes: 74 additions & 0 deletions JSTests/wasm/stress/armv7-fused-branch-compare-unsigned-ge-zero.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//@ requireOptions("--useBBQJIT=1", "--useWasmLLInt=0", "--useOMGJIT=0")

import { instantiate } from "../wabt-wrapper.js"
import * as assert from "../assert.js"

// BBQ fuses a comparison into a following br_if/if and emits the inverted condition, so
// the jump it hands back is the "branch not taken" edge. i64.ge_u inverts to Below, and
// every unsigned value is >= 0, so these reach the never-taken case of
// MacroAssemblerARMv7::branch64(Below, hi, lo, TrustedImm64(0)).
let wat = `
(module
(func (export "geUnsignedZeroBrIf") (param $x i64) (result i32)
(block $done
(br_if $done (i64.ge_u (local.get $x) (i64.const 0)))
(return (i32.const 0))
)
(i32.const 1)
)

;; Constant on the left: i64.le_u inverts to Above, which emitBranchI64 commutes to Below.
(func (export "zeroLeUnsignedBrIf") (param $x i64) (result i32)
(block $done
(br_if $done (i64.le_u (i64.const 0) (local.get $x)))
(return (i32.const 0))
)
(i32.const 1)
)

;; The if-fusion path, which stores the jump in ControlData::m_ifBranch.
(func (export "geUnsignedZeroIf") (param $x i64) (result i32)
(if (result i32) (i64.ge_u (local.get $x) (i64.const 0))
(then (i32.const 1))
(else (i32.const 0))
)
)

;; i64.lt_u inverts to AboveOrEqual, the always-taken sibling of the case above.
(func (export "ltUnsignedZeroBrIf") (param $x i64) (result i32)
(block $done
(br_if $done (i64.lt_u (local.get $x) (i64.const 0)))
(return (i32.const 0))
)
(i32.const 1)
)

;; A non-zero constant is genuinely conditional and misses the compare-with-zero paths.
(func (export "geUnsignedTenBrIf") (param $x i64) (result i32)
(block $done
(br_if $done (i64.ge_u (local.get $x) (i64.const 10)))
(return (i32.const 0))
)
(i32.const 1)
)
)
`

async function test() {
const instance = await instantiate(wat, {}, {})
const { geUnsignedZeroBrIf, zeroLeUnsignedBrIf, geUnsignedZeroIf, ltUnsignedZeroBrIf, geUnsignedTenBrIf } = instance.exports

// Cover values whose high word, low word, or neither is zero.
for (const x of [0n, 1n, 0xffffffffn, 0x100000000n, 0xffffffff00000000n, 0xffffffffffffffffn]) {
assert.eq(geUnsignedZeroBrIf(x), 1)
assert.eq(zeroLeUnsignedBrIf(x), 1)
assert.eq(geUnsignedZeroIf(x), 1)
assert.eq(ltUnsignedZeroBrIf(x), 0)
}

assert.eq(geUnsignedTenBrIf(9n), 0)
assert.eq(geUnsignedTenBrIf(10n), 1)
assert.eq(geUnsignedTenBrIf(0xffffffffffffffffn), 1)
}

await assert.asyncTest(test())
1 change: 1 addition & 0 deletions JSTests/wasm/stress/array-element-creation.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//@ skip if $memoryLimited
//@ runDefault("--useConcurrentJIT=0")

function main() {
Expand Down
Binary file not shown.
Loading