Skip to content

Conversation

@caiiiycuk
Copy link
Contributor

@caiiiycuk caiiiycuk commented Feb 2, 2023

You can download binaries of wasm-opt that support fwasm-exceptions and Asyncify here.
Replace emsdk/upstream/bin/wasm-opt with downloaded version and then you can use -fwasm-exceptions with Asyncify.

--
Hi. This PR does not do lowering wasm exceptions to MVP as suggested in #5343, so I need to explain situation:

I am trying to port a dosbox/dosbox-x core as is to WebAssembly. The dosbox core is syncrhnous, it runs emulation in forever while loop, events are processed inside this loop. To make this loop asynchrous I use Asyncify, however the unmodified core uses exceptions in certain situations.

Asyncify does not support exceptions, this PR should change the situation in some cases including dosbox/x case.
The main idea of emulation loop is follow:

void emulation() {
  do {
    em_sleep(); // <-- **START_UNWIND**
    try {
      int ret = cpudecode();
      // rest logic
    } catch (PageFault&) {
      // load failed page 
    } catch (Some&) {
      // do some 
    }
  } while (ret);
}

int cpudecode() {
  // some logic that also can call **emulation** recursively
}

The catch blocks is used to recover core, or to jump out from some of deep cpudecode calls. There is no situation when emulation loop is called when catch block somewhere in stack:

void some() {
  while () {
    try {
      ...
      some(); // can call start_unwind
    } catch () {
      // never calls to start_unwind
    }
  }
}

This also means that catch block can't change the state, and so only try block can. But on other hand try block it self should works fine with current asyncify code. When we are doing rewind we are only visiting try blocks and never catch blocks, so it work out of the box.

For such type of synchronous code we can add exceptions support, we need only verify that unwind didn't start from a catch block (when catch is not in stack). We can validate this requirment and throw exception if it breaks or ignore the unwind call.

This is very small overhead and simple solution to bring exceptions support in Asyncify, more over for some codebases it should be simple to rewrite the synchronous loop to not call unwind from a catch blocks.

This PR is tested on dosbox-x, it works fine.
emscripten-discuss initial context


There is one thing in impelementation that probably is not correct:

            auto decIf = builder->makeIf(
              br->condition,
              builder->makeSequence(builder->makeDec(amount), br),
              br->value);
            br->condition = nullptr;
            replaceCurrent(decIf);

I have a global variable that I need to decrease on some amount just before br_if instruction,
but only when condition is true. I tried lot of other variants but no luck:

  1. Adding new instruction into condition body:
            Index newLocal = builder->addVar(func, br->condition->type);
            auto setLocal = builder->makeLocalSet(newLocal, br->condition);
            auto getLocal =
              builder->makeLocalGet(newLocal, br->condition->type);
            
            br->condition = builder->makeBlock(
              {setLocal,
               builder->makeIf(getLocal, builder->makeDec(amount)),
               getLocal},
              br->condition->type);

Result:

Assertion failed: (!resultUsed || curr->type != Type::none), function optimize, file Vacuum.cpp, line 95.

>    // resultUsed only makes sense when the type is concrete
>    assert(!resultUsed || curr->type != Type::none);  <-- **ASSERT**
>    // If we actually need the result, then we must not change anything.
>    // TODO: maybe there is something clever though?
>    if (resultUsed) {
>      return curr;
>    }
  1. Just wrap the br_if with new block:
            replaceCurrent(builder->makeSequence(
              builder->makeIf(br->condition, builder->makeDec(amount)), br));

Result:

Assertion failed: (values.size() == 1), function getSingleValue, file wasm-interpreter.h, line 67.

>    if (curr->condition) {
>      Flow conditionFlow = visit(curr->condition);
>      if (conditionFlow.breaking()) {
>        return conditionFlow;
>      }
>      condition = conditionFlow.getSingleValue().getInteger() != 0; // <-- **ASSERT**
  1. Just wrap the br_if but save condition result into local var like in 1. Results is sames as in 2.

  2. Completely replace br_if with if and br:

            auto decIf = builder->makeIf(
              br->condition,
              builder->makeSequence(builder->makeDec(amount), br));
            br->condition = nullptr;
            replaceCurrent(decIf);

Results:

Works fine on small wasm files
On big wasm file have this error:

[wasm-validator error in function CDROM_Interface_Image::LoadCueSheet\28char*\29] unexpected true: if without else must not return a value in body, on

>(if
> (i32.and
>  (i32.load8_u offset=16
>   (i32.add
>    (local.get $8)
>    (i32.load
>     (i32.sub
>      (i32.load
>       (local.get $8)
>      )
>      (i32.const 12)
>     )
>    )
>   )
>  )
>  (i32.const 5)
> )
> (block (result i32)
>  (global.set $__asyncify_catch_counter
>   (i32.sub
>    (global.get $__asyncify_catch_counter)
>    (i32.const 2)
>   )
>  )
>  (br $label$1
>   (i32.const 0)
>  )
> )
>)

OR

[wasm-validator error in function CDROM_Interface_Image::LoadCueSheet\28char*\29] unexpected false: can only drop a valid value, on

>(drop
> (if
>  (i32.and
>   (i32.load8_u offset=16
>    (i32.add
>     (local.get $8)
>     (i32.load
>      (i32.sub
>       (i32.load
>        (local.get $8)
>       )
>       (i32.const 12)
>      )
>     )
>    )
>   )
>   (i32.const 5)
>  )
>  (block (result i32)
>   (global.set $__asyncify_catch_counter
>    (i32.sub
>     (global.get $__asyncify_catch_counter)
>     (i32.const 2)
>    )
>   )
>   (br $label$1
>    (i32.const 0)
>   )
>  )
> )
>)
  1. I thinking to not use replaceCurrent, find the parent block of br_if and insert builder->makeDec just before br, but seems even more cumbersome cause parent block can be Block, Loop, If, Drop, Try, etc. I don't have straightforward algorithm in my mind.

Thanks!

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Interesting idea! I think this makes sense to do.

@caiiiycuk
Copy link
Contributor Author

caiiiycuk commented Feb 8, 2023

Refactored, now I change only top-level catch blocks. Also added lit tests, for me seems correct. Dosbox-x works fine.
Only here could be a problem:

// br_if case with condition and value

            Index newLocal = builder->addVar(func, br->value->type);
            auto setLocal = builder->makeLocalSet(newLocal, br->value);
            auto getLocal = builder->makeLocalGet(newLocal, br->value->type);
            auto condition = br->condition;
            br->condition = nullptr;
            br->value = getLocal;
            auto decIf =
              builder->makeIf(condition,
                              builder->makeSequence(builder->makeDec(), br),
                              getLocal);
            replaceCurrent(builder->makeSequence(setLocal, decIf));

Not sure how to deal if sideeffect will be in setLocal, currenlty it executes before condition....
@kripken please take a look

@allsey87
Copy link

@kripken is there anything preventing this PR from being merged?

@caiiiycuk
Copy link
Contributor Author

I'm sorry, I want to finish this and add the changes suggested by @kripken, I'll try to find some time to finish it (not sure I can do it anytime soon). Btw I've used it many times and it works great in the case of js-dos.

@allsey87
Copy link

Excellent work @caiiiycuk! I look forward to seeing this merged!

@allsey87
Copy link

allsey87 commented Jun 15, 2023

@caiiiycuk I am currently testing your branch and have updated the binaries in emsdk/upstream/bin with those from the build. However I am getting some errors when I try to build my test program:

em++: warning: ASYNCIFY=1 is not compatible with -fwasm-exceptions. Parts of the program that mix ASYNCIFY and exceptions will not compile. [-Wemcc]
error: DISABLE_EXCEPTION_THROWING was set (likely due to -fno-exceptions), which means no C++ exception throwing support code is linked in, but exception catching code appears. Either do not set DISABLE_EXCEPTION_THROWING (if you do want exception throwing) or compile all source files with -fno-except (so that no exceptions support code is required); also make sure DISABLE_EXCEPTION_CATCHING is set to the right value - if you want exceptions, it should be off, and vice versa.

I think the warning is fine. But I am confused as to why DISABLE_EXCEPTION_THROWING is being set. I have added -fwasm-exceptions to both the linker flags and compiler flags (both to the C++ and C flags (I guess the latter is not necessary)). Do have any suggestions on how to fix this? I am using Emscripten 3.1.32.

@caiiiycuk
Copy link
Contributor Author

caiiiycuk commented Jun 15, 2023

I just it like this

            -fwasm-exceptions
            "-sASYNCIFY=1"
            "-sASYNCIFY_IMPORTS=['syncSleep']"
            "-sASYNCIFY_WHITELIST=@${TARGETS_DIR}/dosbox-x-asyncify.txt"

Did you check, maybe you set flag -fno-exceptions somewhere.
Also you need to set -fwasm-exceptions both on compile and linking stages.

@caiiiycuk caiiiycuk changed the title Add partial support for -fwasm-exceptions in Asyncify (#5343) WIP: Add partial support for -fwasm-exceptions in Asyncify (#5343) Jun 22, 2023
@caiiiycuk caiiiycuk marked this pull request as draft June 22, 2023 15:39
@caiiiycuk caiiiycuk changed the title WIP: Add partial support for -fwasm-exceptions in Asyncify (#5343) Add partial support for -fwasm-exceptions in Asyncify (#5343) Jun 22, 2023
@caiiiycuk caiiiycuk force-pushed the fwasm-exceptions branch 3 times, most recently from 7efcd66 to 15bfc21 Compare June 23, 2023 20:44
@caiiiycuk caiiiycuk marked this pull request as ready for review June 23, 2023 20:45
@caiiiycuk
Copy link
Contributor Author

@kripken I apologize for the delay. I have made all the changes you requested. Please take a look and let me know if there's anything else to do.

@caiiiycuk
Copy link
Contributor Author

@kripken friendly ping

@allsey87
Copy link

I have been using this for several weeks now and I haven't found any issues yet! Excellent work @caiiiycuk!

@caiiiycuk
Copy link
Contributor Author

@allsey87 thanks, great to know

@kripken
Copy link
Member

kripken commented Jul 26, 2023

@caiiiycuk Sorry, I've been meaning to get to this but have been busy. Thanks for updating this!

There has recently been a suggestion to make some changes in how wasm exceptions works in the spec, see WebAssembly/exception-handling#280 - I have no idea what will happen there, but it might mean that if we land this we'd need to then refactor it significantly. On the positive side, they do mention it is a goal to make things like Asyncify easier, so perhaps this will become simpler actually. Either way, though, I think we should wait on this PR to see what the spec changes are first, to avoid wasted work for all of us.

@caiiiycuk
Copy link
Contributor Author

@kripken No worries, all good. At least it works for my case. :)

@fs-eire
Copy link

fs-eire commented Jan 20, 2024

Is there any chance that we can merge this? JSPI still need to take some time...

@kripken
Copy link
Member

kripken commented Jan 22, 2024

The new wasm EH proposal I mentioned above is in the process of being implemented now. The main component is this:

binaryen/src/wasm.h

Lines 1473 to 1474 in 3049fb8

// 'try_table' from the new EH proposal
class TryTable : public SpecificExpression<Expression::TryTableId> {

If we want to land this PR then targeting that would make sense, as the old EH will be removed. However, the new wasm EH is not done yet (e.g. parts of the optimizer don't work on it) so it's too early for that, I'm afraid.

@hoodmane
Copy link

hoodmane commented Nov 1, 2025

I think it should be enough because they are Rust projects and they don't use catch_unwind() at all so they have no catch blocks at all.

@curiousdannii
Copy link
Contributor

I'm linking a Rust static lib with C++ code which does use exceptions... But I have no idea how to even find out if this PR would be enough (other than eventually trying it).

@caiiiycuk
Copy link
Contributor Author

@kripken, I rebased it, please review.

@workingjubilee
Copy link

workingjubilee commented Nov 3, 2025

I'm not sure what the standards are for whether or not we block a target change, maybe @workingjubilee can say more about how the Rust project decides this sort of question.

We prefer to make decisions by consensus but in a case where there simply is no way of completely satisfying both parties, we are forced to pick, no? We could pick differently than one prefers, but as mentioned, preference mostly goes out the window if the emsdk simply supports the combinatorics necessary.

However, I’m a bit out of context. Is it okay that it only partially supports exceptions? I mean, the codebase shouldn’t change the Asyncify state from within catch blocks — is that acceptable for the named projects?

I'm not entirely sure, but this sounds like it should be fine. It is fairly rare for Rust code to tamper that much with how unwinding happens, and the result of throwing Rust panics into C++ or C++ exceptions into Rust can be quite explosive. I mean, on the Rust side the program can terminate correctly, but we often wind up picking "terminate" if any other path would be UB, and throwing a foreign exception into C++ can be very that. So it seems to me to be very unlikely that @curiousdannii has been successfully linking with code that extensively micromanages the unwinding state instead of just doing normal throw-catch-maybe-throw-again patterns.

@curiousdannii
Copy link
Contributor

Yeah I haven't been doing anything tricky.

The one catch I see is that indirect calls typically need to be instrumented for Asyncify, and Rust seems to use them a lot. It's very plausible that the Rust exceptions code would use indirect calls. I don't know whether that use would be covered by this PR or not.

You can opt specific functions out of Asyncify instrumentation (asyncify_remove), but it's hard to know when it's safe to. My asyncify_advise log has over 1500 lines.

@workingjubilee
Copy link

Well. Our formatting code typically goes through a vtable for size reasons, which I believe winds up with an indirect call, and while if memory serves the unwinding itself doesn't involve formatting, the panic messaging sure does.

@workingjubilee
Copy link

workingjubilee commented Nov 3, 2025

It is a tad too late in the day for me to explain in detail what the difference is between "starting a panic", "running the panic handler" (which usually is the thing that does the message printing, if I am not somehow misremembering everything...), and "unwinding (due to a panic)" but these are technically all their own little thing and it's not entirely clear to me what ASYNCIFY needs and doesn't need, here.

This is why "inscrutable binary editing with unclear parameters but it works, probably" is usually something to which we say, "Have fun with that!" Except that it is apparently core to some usage of the emscripten target, and, well, if we don't support what the emsdk can do, then what exactly does define the emscripten target for us? That is a philosophical question which may be useful to address one day but also may be nice to kick down the road a bit.

@caiiiycuk
Copy link
Contributor Author

caiiiycuk commented Nov 3, 2025

Yes, sometimes it’s really hard to understand all the technical details, especially when you don’t fully know the supporting technology.

If we simplify things a lot, code like this is considered normal:

while (true) {
    try {
       // ...
       asyncify_sleep(10);
    } catch (...) {
       // anything
    }
}

while code like this is not supported:

while (true) {
    try {
       // ...
       asyncify_sleep(10);
    } catch (...) {
       // anything
       asyncify_sleep(10); // <--- not supported
    }
}

I believe this approach will succeed because code is typically written like in block 1 — a catch block is used to handle an exception and then return the execution flow to a valid, normal state.

At least in the case of incorrect code organization, we’ll see a clear, understandable error indicating that asyncify_sleep was called in the wrong place.

Overall, if we look only at Emscripten, I don’t see any negative side effects from merging this PR. I’ve been maintaining it for about two years without major issues.

};

// Add catch block counters to verify that unwind is not called from catch
// block.
Copy link
Member

Choose a reason for hiding this comment

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

Could we depend on the IR structure to find if we are inside a catch? See ExpressionStackWalker which maintains a stack of parents. findBreakTarget() there shows an example of scanning the parents.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, I will try to adopt this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kripken

I have been thinking about this task, and so far I do not understand how to make it more efficient. We do not actually need a parent stack, because we do not care which nested catch block we are currently in. Essentially, I only want to know whether we are inside a catch or not. In the current code, we have a counter for this, which is effectively just a boolean flag. If I am not mistaken, it is always either 0 or 1, which means we could add additional assert that checks it falls within that range.

The core problem is that at compile time we cannot determine whether a function that changes the Asyncify state called inside a catch or not. More precisely, we could determine that if there were no virtual function calls. I have not yet found a way to work around this at compile time, which is why a counter is used.

I still do not quite understand how to apply a parent stack here. I could maintain such a stack at runtime, but that seems worse than just using an integer counter.

Or is the idea to keep the counter, but update it using the information obtained from ExpressionStackWalker?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, obviously it can be greater than 1, but at least it cannot be less than 0.

Copy link
Member

Choose a reason for hiding this comment

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

The core problem is that at compile time we cannot determine whether a function that changes the Asyncify state called inside a catch or not.

We may not know if there is a catch further up, at runtime, but we will reach it when we unwind, I think? So the compile-time knowledge seems enough. Here is what I mean:

function foo() {
  try {
    ..
  } catch (e) {
    bar();
    // after call to bar
  }
}

function bar() {
  importThatPauses();
}

Say we reach that call to bar, then it calls the import that pauses. Inside bar, we don't know that we are inside a catch from earlier. But after bar unwinds normally, we reach "after call to bar" and we can have a static assert there about not unwinding.

That is, each catch block can, statically, get added checks to not unwind, after each possibly-unwinding operation there?

Copy link
Member

Choose a reason for hiding this comment

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

(and iirc we already have a way to add checks for "should not be unwinding here", so I hope this is not hard to add)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh, sounds correct. But then we cannot support --pass-arg=asyncify-ignore-unwind-from-catch if I understood correctly. But we can add those static checks in case if this flag is not passed.

Copy link
Member

Choose a reason for hiding this comment

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

I see now, thanks. So that option makes it necessary to know immediately if we are nested under a catch...

caiiiycuk and others added 3 commits November 3, 2025 22:17
Co-authored-by: Alon Zakai <alonzakai@gmail.com>
Co-authored-by: Alon Zakai <alonzakai@gmail.com>
Co-authored-by: Alon Zakai <alonzakai@gmail.com>
@curiousdannii
Copy link
Contributor

At least in the case of incorrect code organization, we’ll see a clear, understandable error indicating that asyncify_sleep was called in the wrong place.

At compile time or run time?

@caiiiycuk
Copy link
Contributor Author

At least in the case of incorrect code organization, we’ll see a clear, understandable error indicating that asyncify_sleep was called in the wrong place.

At compile time or run time?

At runtime. However, you can also avoid this by using a special flag that ignores asyncify_sleep in incorrect situations — though the app might get stuck there in forever loop.

@curiousdannii
Copy link
Contributor

Specifically asyncify_sleep or all async functions?

The other big question is if you think async in catch is a fundamental incompatibility, or instead a big difficulty that might be supportable with a lot more work?

@fs-eire
Copy link

fs-eire commented Nov 3, 2025

Is it possible to use any compile time analysis tool or runtime check to tell whether any rewind/unwind happening inside a catch block?

@caiiiycuk
Copy link
Contributor Author

Specifically asyncify_sleep or all async functions?

The other big question is if you think async in catch is a fundamental incompatibility, or instead a big difficulty that might be supportable with a lot more work?

Any async function — they’re all implemented the same way. But I don’t know if it’s even possible to make Asyncify work inside a catch block. There’s also an attempt here: #5143 — maybe if that idea is developed further, something could come out of it. Still, I think Asyncify is rarely used in practice, and Asyncify inside a catch block is an even rarer case. It seems easier to just rewrite the code to avoid the need for infinite loops inside catch blocks altogether.

Is it possible to use any compile time analysis tool or runtime check to tell whether any rewind/unwind happening inside a catch block?

Yes, to some extent — it’s similar to automatically generating a list of functions that modify asyncify_state. But if there’s a virtual function call inside (through a pointer), then you really can’t be sure anymore.

@caiiiycuk
Copy link
Contributor Author

caiiiycuk commented Nov 4, 2025

Is it possible to use any compile time analysis tool or runtime check to tell whether any rewind/unwind happening inside a catch block?

Potentially, it’s possible to implement a check with two outcomes:

  1. 100% no catch block calls an async function (ALL GOOD)
  2. Some functions that modify the asyncify state might be called from catch + list of this functions.

Comment on lines 1251 to 1252
// Add catch block counters to verify that unwinding is not done from catch
// block.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Add catch block counters to verify that unwinding is not done from catch
// block.
// Add catch block counters to verify that unwinding is not done from inside
// a catch block. Note that we need to know if we are nested under a catch
// immediately - we can't depend on unwinding noticing it when we reach
// that parent catch - because of the option asyncify-ignore-unwind-from-catch.

// - break outside top-level/nested catch 0 (ignored)
//
// - exiting from top-level catch -1
// - exiting from nested catch 0 (ignored)
Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately, detecting an exit from a catch is not as simple as this. You would also need to handle br_on and return_call, off the top of my head.

We could in theory say this feature doesn't support GC or tail calls, but imagine this is linked with a module using those features, and then inlining happens, so it seems worrying.

How important is asyncify-ignore-unwind-from-catch? Without that, we could use the more static approach I mentioned before.

Copy link
Member

Choose a reason for hiding this comment

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

(Oh, and also try_table - people could mix exnref EH and non-exnref EH.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Personally I don't use asyncify-ignore-unwind-from-catch, I provided it for completeness, and because it is easy. So, I think can remove it, but can't guess if it can be used by someone else. Also I think it is easy to fix in code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I mean if needed devs can fix code to support something like ignore this asyncify_sleep

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I don't think anyone else mentioned a need for that option, so maybe it is best to remove it. Can maybe wait a bit to see if anyone on this PR responds.

@workingjubilee
Copy link

while code like this is not supported:

while (true) {
    try {
       // ...
       asyncify_sleep(10);
    } catch (...) {
       // anything
       asyncify_sleep(10); // <--- not supported
    }
}

I believe this approach will succeed because code is typically written like in block 1 — a catch block is used to handle an exception and then return the execution flow to a valid, normal state.

Hmm, that may be slightly easier to run into than I thought! Though I still wouldn't expect it to happen via the Rust side because of how Rust code uses exceptions only, well, exceptionally.

@curiousdannii It may be possible to mitigate the indirection problem if necessary... or at least help you with the ASYNCIFY_REMOVE list... so please feel free to ask us about this if you wind up having troubles after updating to an emcc with this branch in it. Though not in this thread, obviously, I mean a new issue or asking on Zulip or such.

@curiousdannii
Copy link
Contributor

curiousdannii commented Nov 7, 2025

Even with JS exceptions I need to work out what I can asyncify_remove. Here's what I'm working with. Wish me luck 😂
glulxe-log.txt

Edit: great news: I've discovered that at least one of the apps I build in my project can go back to using ASYNCIFY_IGNORE_INDIRECT. I'd disabled it when I first switched to Rust. Possibly changing to panic=abort made the difference.

@caiiiycuk
Copy link
Contributor Author

@kripken

Please take a look at the last two commits. I attempted to implement the following algorithm:

  1. We iterate over all function calls.
  2. We determine whether the call is made inside a catch block.
  3. If it is, we insert a check after the call to ensure that ASYNCIFY_STATE is still NORMAL.

More details on point 2, since I am not entirely sure the current implementation covers all cases—mainly because I may not be aware of every detail.

I use ExpressionStackWalker to locate the nearest parent Try block. Once it is found, I need to determine whether the call occurs inside the try body or inside one of the catch blocks. In practice, it is enough to check whether the current instruction belongs to the try.body. To do this, I simply take the first child of the Try block from the stack, and if that child is try->body, then the call is considered safe; otherwise, I insert the corresponding check.

I tested this code with js-dos, and so far I haven’t encountered any issues. However, I am still not completely confident in it overall.

Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Looks pretty good! I don't see anything incorrect.

//
// When an unwind operation is triggered from inside a wasm-exceptions
// catch block, which is not supported, silently ignore it rather than
// fail during rewinding later.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// fail during rewinding later.
// fail during rewinding later. (This is unsafe in general.)

#include "ir/memory-utils.h"
#include "ir/module-utils.h"
#include "ir/names.h"
#include "ir/parents.h"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#include "ir/parents.h"

#include "cfg/liveness-traversal.h"
#include "ir/branch-utils.h"
#include "ir/effects.h"
#include "ir/eh-utils.h"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#include "ir/eh-utils.h"


#include "asmjs/shared-constants.h"
#include "cfg/liveness-traversal.h"
#include "ir/branch-utils.h"
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#include "ir/branch-utils.h"

return std::make_unique<AsyncifyAssertUnwindCorrectness>(analyzer, module);
}

bool isFunctionParallel() override { return true; }
Copy link
Member

Choose a reason for hiding this comment

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

As a convention we prefer this at the top of the class, before all other internals.

}

void visitCall(Call* curr) {
assert(!expressionStack.empty());
Copy link
Member

Choose a reason for hiding this comment

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

A return_call (curr->isReturn) can be ignored here: It returns first, leaving the Catch, before calling.

bool isFunctionParallel() override { return true; }

void runOnFunction(Module*, Function* function) override {
auto builder = std::make_unique<Builder>(*module);
Copy link
Member

Choose a reason for hiding this comment

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

Builders are very cheap to construct - please create one in replaceCallWithCheck, as needed, which would be simpler.

void addFunctions(Module* module, bool asserts) {
Builder builder(*module);
auto makeFunction = [&](Name name, bool setData, State state) {
auto* body = builder.makeBlock();
Copy link
Member

Choose a reason for hiding this comment

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

Why move this?

}

void addFunctions(Module* module) {
void addFunctions(Module* module, bool asserts) {
Copy link
Member

Choose a reason for hiding this comment

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

New param seems unused?

PassRunner runner(module);
runner.add(std::make_unique<AsyncifyAssertInNonInstrumented>(
&analyzer, pointerType, asyncifyMemory));
if (asserts) {
Copy link
Member

Choose a reason for hiding this comment

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

This is already inside an if (asserts), line 1860

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.

8 participants