Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 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
3183548
Testmasm and debug-build fixes; The ASSERT was removed upstream.
justinmichaud Aug 13, 2026
4c3f504
Fix instanceof checkpointing on armv7; We continue with incorrect spe…
justinmichaud Aug 13, 2026
fb468a0
Fix 32-bit IC register clobbers.
justinmichaud Aug 13, 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
81 changes: 81 additions & 0 deletions JSTests/stress/get-by-val-buffered-identifier-lifetime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//@ requireOptions("--useZombieMode=1", "--repatchBufferingCountdown=10")

// A by-val inline cache buffers (structure, property name) pairs while it waits for enough cases to
// generate a stub, and every later miss on the site compares against all of them. The subscripts
// here are fresh strings that nothing else refers to once the call returns, so the buffered pairs
// have to keep working across collections that sweep those strings.

function shouldBe(actual, expected) {
if (actual !== expected)
throw new Error("bad value: expected " + expected + " but got " + actual);
}

var prefix = "ta";
var suffix = "rgetx";

// A rope, which toPropertyKey resolves in place, so each call caches a different string cell.
function freshKey() {
return prefix + suffix.substring(0, 4);
}

function makeSite() {
return new Function("o", "k", "return o[k];");
}

function makeBase(shape, value) {
var o = {};
for (var i = 0; i <= shape; ++i)
o["pad" + shape + "_" + i] = i;
o.target = value;
o[0] = value + 1000;
return o;
}

var siteCount = 16;
var shapeCount = 6;
var sites = [];
var bases = [];
for (var s = 0; s < siteCount; ++s) {
sites.push(makeSite());
var group = [];
for (var shape = 0; shape < shapeCount; ++shape)
group.push(makeBase(shape + s * shapeCount, shape));
bases.push(group);
}

function churn() {
var last = "";
for (var i = 0; i < 20000; ++i)
last = ("a" + i) + ("b" + i);
return last.length;
}

for (var round = 0; round < 40; ++round) {
for (var s = 0; s < siteCount; ++s) {
var site = sites[s];
var group = bases[s];
for (var i = 0; i < 24; ++i) {
var shape = i % shapeCount;
shouldBe(site(group[shape], freshKey()), shape);
}
}

churn();
gc();
churn();
edenGC();

// Integer subscripts compare against every buffered pair on the site.
for (var s = 0; s < siteCount; ++s) {
var group = bases[s];
for (var i = 0; i < shapeCount; ++i)
shouldBe(sites[s](group[i], 0), i + 1000);
}

// String subscripts compare against them too, with keys that are fresh cells again.
for (var s = 0; s < siteCount; ++s) {
var group = bases[s];
for (var i = 0; i < shapeCount; ++i)
shouldBe(sites[s](group[i], freshKey()), i);
}
}
17 changes: 17 additions & 0 deletions JSTests/stress/inline-cache-proxy-and-getter-spill-state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// A stub records one spill state shared by all of its access cases, so every case that calls out
// to JS has to spill the same registers. This inline cache sees both a Proxy and a JS getter.
const target = { p: 1 };
const proxy = new Proxy(target, { get(t, k) { return t[k]; } });
const withGetter = {};
Object.defineProperty(withGetter, "p", { get: function () { return 2; } });

function f(o) { return o.p; }
noInline(f);

globalThis.testLoopCount ??= 1e4;
for (let i = 0; i < testLoopCount; ++i) {
if (f(proxy) !== 1)
throw new Error("proxy load returned the wrong value");
if (f(withGetter) !== 2)
throw new Error("getter load returned the wrong value");
}
36 changes: 36 additions & 0 deletions JSTests/stress/instanceof-osr-exit-from-inlined-proxy-get.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//@ runDefault("--forceOSRExitToLLInt=true", "--thresholdForJITAfterWarmUp=10", "--thresholdForOptimizeAfterWarmUp=20")

// The DFG inlines a proxy's get trap for the Symbol.hasInstance and prototype reads that instanceof
// performs. An OSR exit out of that inlined call returns into the LLInt at op_instanceof's return
// location, which has to finish the operation rather than fall through to the next opcode with the
// destination register still holding whatever was there before.

function assert(b) {
if (!b)
throw new Error("Bad assertion");
}

function test(f) {
for (let i = 0; i < 1000; i++)
f();
}

// A pass-through proxy, so this instanceof is genuinely true and the site caches a hit first.
test(function() {
let proxy = new Proxy(function () { }, { });
assert(new proxy instanceof proxy);
});

// The trap hands back a fresh object every time "prototype" is read, so the instance is never an
// instanceof the proxy.
test(function() {
let handler = {
get: function(target, prop) {
if (prop === "prototype")
return { };
return target[prop];
}
};
let proxy = new Proxy(function () { }, handler);
assert(!(new proxy instanceof proxy));
});
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!");
22 changes: 22 additions & 0 deletions JSTests/stress/op-catch-restores-metadata-table-register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// A strict-mode indexed store whose Proxy set trap returns false throws out of the by-val inline
// cache. Nothing between the throw and the handler copies the handler frame's callee saves into the
// entry frame buffer, so op_catch restores a metadata table belonging to another CodeBlock and has to
// rematerialize metadataTableRegister. A named store (proxy.x) takes a different path and does not
// reach this, so keep the subscript numeric.

globalThis.testLoopCount ??= 1e4;

(function() {
"use strict";
const proxy = new Proxy({}, { set: function() { return false; } });
for (let i = 0; i < testLoopCount; ++i) {
let threw = false;
try {
proxy[42] = 40;
} catch (e) {
threw = e instanceof TypeError;
}
if (!threw)
throw new Error("strict indexed store through a rejecting proxy set trap should throw a TypeError");
}
})();
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);
}
Loading