Skip to content

Conversation

@RReverser
Copy link
Collaborator

Similar to #24291, but for the transform handling -pthread + -sALLOW_MEMORY_GROWTH.

In the first commit I added a codesize variation to commit current sizes, and in the second you can see that this transform actually saves the total size in addition to making acorn-optimizer simpler.

@RReverser RReverser requested review from kripken and sbc100 May 9, 2025 22:30
Copy link
Collaborator

@sbc100 sbc100 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@RReverser RReverser force-pushed the simplify-growable-heap branch from d2c16fe to 767a4eb Compare May 9, 2025 22:49
@RReverser

This comment was marked as outdated.

@RReverser RReverser force-pushed the simplify-growable-heap branch from 02aa81c to 3f1ecd4 Compare May 10, 2025 02:03
@RReverser RReverser marked this pull request as draft May 11, 2025 09:43
@RReverser
Copy link
Collaborator Author

Converted to draft because I thought of some cases where this change can be problematic without further work, even though those cases are not yet covered by tests.

SAFE_HEAP one should be still good to go.

@RReverser RReverser marked this pull request as ready for review May 12, 2025 17:41
@RReverser
Copy link
Collaborator Author

Converted to draft because I thought of some cases where this change can be problematic without further work, even though those cases are not yet covered by tests.

Converted to ready for review because turns out it's not a regression, the edge case I was thinking of (memory growth + ASAN/SAFE_HEAP) is already not working as expected and will be addressed separately in #24309.

sbc100 pushed a commit that referenced this pull request May 13, 2025
Leverages ASan's C API for arbitrary loads/stores instead of custom C
code with separate variants for each possible type.

Similar to #24295 and
#24291.
@RReverser RReverser force-pushed the simplify-growable-heap branch from 3f1ecd4 to ede14eb Compare May 13, 2025 17:52
@RReverser
Copy link
Collaborator Author

I want to do a bit more testing here just in case, so holding off merging for now.

@RReverser
Copy link
Collaborator Author

RReverser commented May 13, 2025

Yessss. After investigating #24309, I realised another issue with this refactoring, and I knew this should be failing in some cases, but couldn't make it break any tests.

Turns out, it's tricky to trigger that edge case due to pthreads involved and due to Emscripten accessing the heap in a lot of functions (and, thus, updating it even before it's actually needed), but finally managed to craft a test that breaks after this transform.

One of those rare feelings when things work but they really shouldn't 😅 I'll submit a new test either in this PR or separately.

@RReverser RReverser force-pushed the simplify-growable-heap branch from 0534aec to 1e24d3c Compare May 14, 2025 15:24
@RReverser
Copy link
Collaborator Author

Ok the tests look pretty different now because majority of Emscripten APIs that are implemented in JS touch HEAP in one way or another, and so even logging via printf/puts or macro like EM_ASM would hide the issue by updating the heap bindings too early.

I had to drop all the way down to EM_JS + atomics, which successfully broke all those tests as expected (showing that memory view is not updated on the first access), and subsequently fixed them by changing transform from GROWABLE_HEAP(HEAPxx) to (GROWABLE_HEAP(), HEAPxx) which ensures that the heap value is always retrieved after updating the views, and not evaluated before that.

@kripken @sbc100 I'm waiting for the CI to see if this uncovered anything else, but otherwise should be ready for another look.

@sbc100
Copy link
Collaborator

sbc100 commented May 14, 2025

Ok the tests look pretty different now because majority of Emscripten APIs that are implemented in JS touch HEAP in one way or another, and so even logging via printf/puts or macro like EM_ASM would hide the issue by updating the heap bindings too early.

I had to drop all the way down to EM_JS + atomics, which successfully broke all those tests as expected (showing that memory view is not updated on the first access), and subsequently fixed them by changing transform from GROWABLE_HEAP(HEAPxx) to (GROWABLE_HEAP(), HEAPxx) which ensures that the heap value is always retrieved after updating the views, and not evaluated before that.

I don't understand why you can't use use the return value of GROWABLE_HEAP(HEAPxx).. can you explain a little more? Is the problem that incoming argument to GROWABLE_HEAP would somehow be wrong?

@kripken @sbc100 I'm waiting for the CI to see if this uncovered anything else, but otherwise should be ready for another look.

@RReverser
Copy link
Collaborator Author

RReverser commented May 14, 2025

I don't understand why you can't use use the return value of GROWABLE_HEAP(HEAPxx).. can you explain a little more? Is the problem that incoming argument to GROWABLE_HEAP would somehow be wrong?

Arguments are evaluated before the function itself, which is what I was trying to demonstrate & catch with the updated tests.

If you do

let gotHeap = GROWABLE_HEAP(HEAP8);

then it's equivalent to

let gotHeap = (function GROWABLE_HEAP(arr) {
  // inside GROWABLE_HEAP:
  HEAP8 = new Uint8Array(...);
  HEAP16 = ...;
  ...;
  return arr;
})(HEAP8);

which in turn is like

let arr = HEAP8;
HEAP8 = new Uint8Array(...);
HEAP16 = ...;
...;
let gotHeap = arr; // the old value of HEAP8, not the one from assignment above

Meanwhile, doing

let gotHeap = (GROWABLE_HEAP(), HEAP8);

is like

HEAP8 = new Uint8Array(...);
HEAP16 = ...;
...;
let gotHeap = HEAP8;

which is exactly what we want.

@sbc100
Copy link
Collaborator

sbc100 commented May 14, 2025

I see. In that case perhaps we can rename GROWABLE_HEAP() to something more meaningful? "MAYBE_UPDATE_HEAP_VIEWS" (seems a bit lot). CHECK_MEM_GROWTH (better?)?

@RReverser
Copy link
Collaborator Author

In that case perhaps we can rename GROWABLE_HEAP() to something more meaningful? "MAYBE_UPDATE_HEAP_VIEWS" (seems a bit lot). CHECK_MEM_GROWTH (better?)?

Yeah, either is fine by me. I was thinking of maybeUpdateMemoryViews() to be in line with updateMemoryViews() but wasn't sure if worth it.

@RReverser
Copy link
Collaborator Author

In that case perhaps we can rename GROWABLE_HEAP() to something more meaningful? "MAYBE_UPDATE_HEAP_VIEWS" (seems a bit lot). CHECK_MEM_GROWTH (better?)?

Yeah, either is fine by me. I was thinking of maybeUpdateMemoryViews() to be in line with updateMemoryViews() but wasn't sure if worth it.

I'll just do this, especially since this function is no longer exposed as runtime symbol and doesn't need to have the uppercase name.

@sbc100
Copy link
Collaborator

sbc100 commented May 14, 2025

Yeah I guess there is no reason to do with all caps here.

@sbc100
Copy link
Collaborator

sbc100 commented May 14, 2025

Since this symbol will appear a lot in unminified output is it worth picking a short-ish name? Maybe not?

@RReverser
Copy link
Collaborator Author

Eh, personally I'd prioritise debuggability over code size for unminified JS.

@RReverser
Copy link
Collaborator Author

FWIW I'll probably merge / rename those functions a bit more as part of fixing #24309 anyway.

@RReverser
Copy link
Collaborator Author

CI looks good and I feel more confident in this change now 🤞

@RReverser RReverser force-pushed the simplify-growable-heap branch from 1c450c2 to 54031be Compare May 16, 2025 00:06
@RReverser RReverser enabled auto-merge (squash) May 16, 2025 00:09
@RReverser
Copy link
Collaborator Author

FAIL [0.002s]: test_async_hello_v8 (test_core.instance) looks like a broken test on main branch.

@kripken
Copy link
Member

kripken commented May 16, 2025

Trying to read the changes since my last review, I have a problem that I've seen in @sbc100 's PRs as well with force-pushes... how can I see only the last changes? The last "compare" button shows what appears to be a merge commit from main, and nothing else, and otherwise I see 13 force-pushed commits but I can't tell which are different from before and which (if any) are new compared to before.

In general I think using normal merge commits, not force-pushes, avoids such problems. Is there another way to read the diff here?

@sbc100
Copy link
Collaborator

sbc100 commented May 16, 2025

Trying to read the changes since my last review, I have a problem that I've seen in @sbc100 's PRs as well with force-pushes... how can I see only the last changes? The last "compare" button shows what appears to be a merge commit from main, and nothing else, and otherwise I see 13 force-pushed commits but I can't tell which are different from before and which (if any) are new compared to before.

In general I think using normal merge commits, not force-pushes, avoids such problems. Is there another way to read the diff here?

It looks like he did squash the commits so if you want to do an incremental review you could just look at the later commits.

Obviously its not ideal because (a) you have to trust the earlier commits are not updated and (b) you need to keep track or remember which commit you last reviewed. But as somehow a very much a rebase workflow person, I'm hesitant to force folks into a merge workflow.

@sbc100
Copy link
Collaborator

sbc100 commented May 16, 2025

Personally I always like to review the whole change each time, just to remind myself what its doing as a whole.

@RReverser
Copy link
Collaborator Author

Hm I don't think I did a squash, just the "update branch with rebase" via Github. Those are annoying too on Github, but the actual commits should be still the same as before.

@kripken this last commit is the only change since last review: 54031be

@kripken
Copy link
Member

kripken commented May 16, 2025

Ok, thanks, last commit looks good.

RReverser added 13 commits May 16, 2025 20:28
Similar to emscripten-core#24291, but for the transform handling `-pthread` + `-sALLOW_MEMORY_GROWTH`.

In the first commit I added a codesize variation to commit current sizes, and in the second you can see that this transform actually saves the total size in addition to making acorn-optimizer simpler.
Memory views are automatically and lazily updated by the HEAPxx[...] transform, so this unconditional transform is not necessary.
@RReverser RReverser force-pushed the simplify-growable-heap branch from 54031be to bdee9d8 Compare May 16, 2025 19:28
@RReverser RReverser merged commit d4cd0c7 into emscripten-core:main May 16, 2025
28 checks passed
@RReverser RReverser deleted the simplify-growable-heap branch May 16, 2025 21:38
RReverser added a commit that referenced this pull request May 21, 2025
While working on #24295, I noticed I could accidentally prevent ASan and SAFE_HEAP from finding any `HEAPxx[n]` accesses from JS when `ALLOW_MEMORY_GROWTH` is enabled, and no tests would catch it, because no tests verify that acorn-optimizer passes which change `HEAPxx[n]` into something else can work in conjunction with each other.

After adding the test, I found that an existing `SUPPORT_BIG_ENDIAN` feature was already broken in the same way, because it transforms `HEAPxx[n]` accesses into DataView methods, and since it was running before ASan and SAFE_HEAP, those latter transforms became no-ops. I fixed that in the same PR by moving the big-endian transform to the end.

In the future we might want to consolidate those transforms in a more reliable way (e.g. only run a single code transform that does `HEAPxx[n]` -> `__loadHeap`/`__storeHeap` where those functions do all the work using `#if`s), but at least for now this PR fixes an immediate problem and catches potential future regressions.
RReverser added a commit that referenced this pull request May 21, 2025
)

While working on #24295, I noticed I could accidentally prevent ASan and
SAFE_HEAP from finding any `HEAPxx[n]` accesses from JS when
`ALLOW_MEMORY_GROWTH` is enabled, and no tests would catch it, because
no tests verify that acorn-optimizer passes which change `HEAPxx[n]`
into something else can work in conjunction with each other.

After adding the test, I found that an existing `SUPPORT_BIG_ENDIAN`
feature was already broken in the same way, because it transforms
`HEAPxx[n]` accesses into DataView methods, and since it was running
before ASan and SAFE_HEAP, those latter transforms became no-ops. I
fixed that in the same PR by moving the big-endian transform to the end.

In the future we might want to consolidate those transforms in a more
reliable way (e.g. only run a single code transform that does
`HEAPxx[n]` -> `__loadHeap`/`__storeHeap` where those functions do all
the work using `#if`s), but at least for now this PR fixes an immediate
problem and catches potential future regressions.
Lukasdoe pushed a commit to Lukasdoe/emscripten that referenced this pull request Jun 19, 2025
…cripten-core#24309)

While working on emscripten-core#24295, I noticed I could accidentally prevent ASan and
SAFE_HEAP from finding any `HEAPxx[n]` accesses from JS when
`ALLOW_MEMORY_GROWTH` is enabled, and no tests would catch it, because
no tests verify that acorn-optimizer passes which change `HEAPxx[n]`
into something else can work in conjunction with each other.

After adding the test, I found that an existing `SUPPORT_BIG_ENDIAN`
feature was already broken in the same way, because it transforms
`HEAPxx[n]` accesses into DataView methods, and since it was running
before ASan and SAFE_HEAP, those latter transforms became no-ops. I
fixed that in the same PR by moving the big-endian transform to the end.

In the future we might want to consolidate those transforms in a more
reliable way (e.g. only run a single code transform that does
`HEAPxx[n]` -> `__loadHeap`/`__storeHeap` where those functions do all
the work using `#if`s), but at least for now this PR fixes an immediate
problem and catches potential future regressions.
juj pushed a commit that referenced this pull request Sep 10, 2025
This PR fixes issues with big endian support introduced in the following
PRs:
- #24295 - The change did not consider the `littleEndianHeap` transform
implications,
- #24283 - The new LE_HEAP* functions were not added to link.py, causing
runtime failure on BE system.

The `littleEndianHeap` transform code is simplified and updated to
detect the code introduced by `growableHeap` pass.
A new test was added to verify the combined transformation generates
working code.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants