Skip to content

Invalid WASM produced for a certain combination of flow and exception handling #62961

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
SingleAccretion opened this issue May 26, 2023 · 3 comments
Labels
backend:WebAssembly confirmed Verified by a second party

Comments

@SingleAccretion
Copy link
Contributor

SingleAccretion commented May 26, 2023

Reproduction (.ll-only, the original case is from a custom IR-producing compiler):

target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-emscripten"

declare i32 @__gxx_wasm_personality_v0(...)
declare void @llvm.wasm.rethrow()
declare ptr @llvm.wasm.get.exception(token)
declare i32 @GetReturnCode(i32)
declare void @RhpFallbackFailFast()

define i32 @Test(ptr %0) #2 personality ptr @__gxx_wasm_personality_v0 {
BB00:
  %1 = alloca i32, align 4
  store i32 0, ptr %1, align 4
  %2 = alloca ptr, align 4
  store ptr null, ptr %2, align 4
  %3 = freeze i32 undef
  %4 = alloca i32, align 4
  store i32 %3, ptr %4, align 4
  %5 = alloca ptr, align 4
  store ptr null, ptr %5, align 4
  %6 = freeze i32 undef
  %7 = alloca i32, align 4
  store i32 %6, ptr %7, align 4
  %8 = alloca ptr, align 4
  store ptr null, ptr %8, align 4
  %9 = alloca ptr, align 4
  store ptr null, ptr %9, align 4
  %10 = alloca ptr, align 4
  store ptr %0, ptr %10, align 4
  %11 = alloca { { ptr, i32 }, ptr }, align 8
  br label %BB01

BB01:                                             ; preds = %BB00
  store i32 0, ptr %0, align 4
  %12 = getelementptr i8, ptr %0, i32 4
  store ptr null, ptr %12, align 4
  %13 = getelementptr i8, ptr %0, i32 12
  store ptr null, ptr %13, align 4
  %14 = getelementptr i8, ptr %0, i32 20
  store ptr null, ptr %14, align 4
  %15 = getelementptr i8, ptr %0, i32 24
  store ptr null, ptr %15, align 4
  br label %BB02

BB02:                                             ; preds = %BB01
  br label %BB03

BB03:                                             ; preds = %BB02
  br label %BB04

BB04:                                             ; preds = %BB03
  %16 = getelementptr i8, ptr %0, i32 28
  %17 = invoke i32 @GetReturnCode(i32 1)
          to label %18 unwind label %BT00

18:                                               ; preds = %BB04
  %19 = getelementptr i8, ptr %0, i32 8
  store i32 %17, ptr %19, align 4
  %20 = getelementptr i8, ptr %0, i32 8
  %21 = load i32, ptr %20, align 4
  store i32 %21, ptr %0, align 4
  br label %BB08

BB08:                                             ; preds = %25, %18
  %22 = load i32, ptr %0, align 4
  ret i32 %22

BB06:                                             ; preds = %BBDS
  %23 = getelementptr i8, ptr %0, i32 28
  %24 = invoke i32 @GetReturnCode(i32 2)
          to label %25 unwind label %BT01

25:                                               ; preds = %BB06
  %26 = getelementptr i8, ptr %0, i32 16
  store i32 %24, ptr %26, align 4
  %27 = getelementptr i8, ptr %0, i32 16
  %28 = load i32, ptr %27, align 4
  store i32 %28, ptr %0, align 4
  br label %BB08

BT01:                                             ; preds = %49, %40, %BT00, %BB06
  %29 = catchswitch within none [label %30] unwind to caller

30:                                               ; preds = %BT01
  %31 = catchpad within %29 [ptr null]
  %32 = call ptr @llvm.wasm.get.exception(token %31)
  store ptr %32, ptr %11, align 4
  %33 = getelementptr i8, ptr %11, i32 8
  store ptr null, ptr %33, align 4
  %34 = getelementptr i8, ptr %0, i32 28
  %35 = call i32 @GetReturnCode(i32 0) [ "funclet"(token %31) ]
  %36 = icmp eq i32 %35, 0
  br i1 %36, label %38, label %37

37:                                               ; preds = %30
  catchret from %31 to label %BBDS

38:                                               ; preds = %30
  call void @llvm.wasm.rethrow() [ "funclet"(token %31) ]
  unreachable

BT00:                                             ; preds = %BB04
  %39 = catchswitch within none [label %40] unwind label %BT01

40:                                               ; preds = %BT00
  %41 = catchpad within %39 [ptr null]
  %42 = call ptr @llvm.wasm.get.exception(token %41)
  store ptr %42, ptr %11, align 4
  %43 = getelementptr i8, ptr %11, i32 8
  store ptr null, ptr %43, align 4
  %44 = getelementptr i8, ptr %0, i32 28
  %45 = invoke i32 @GetReturnCode(i32 0) [ "funclet"(token %41) ]
          to label %46 unwind label %BT01

46:                                               ; preds = %40
  %47 = icmp eq i32 %45, 0
  br i1 %47, label %49, label %48

48:                                               ; preds = %46
  catchret from %41 to label %BBDS

49:                                               ; preds = %46
  invoke void @llvm.wasm.rethrow() [ "funclet"(token %41) ]
          to label %50 unwind label %BT01

50:                                               ; preds = %49
  unreachable

BBDS:                                             ; preds = %48, %37
  %51 = phi i32 [ %35, %37 ], [ %45, %48 ]
  switch i32 %51, label %BBFF [
    i32 1, label %BB06
  ]

BBFF:                                             ; preds = %BBDS
  call void @RhpFallbackFailFast()
  unreachable
}

attributes #2 = { noinline optnone "target-features"="+exception-handling,+mutable-globals,+sign-ext" }
llvm-as repro.ll
emcc -c -fwasm-exceptions -g3 repro.bc -o repro.o
wasm-validate --enable-exceptions repro.o

Expected behavior: WASM validates.
Actual behavior:

repro.o:00001e0: error: type mismatch in br_table, expected [i32] but got []
repro.o:00001e0: error: br_table labels have inconsistent types: expected [i32], got []
repro.o:00001e0: error: br_table labels have inconsistent types: expected [i32], got []

Commit on which this was reproduced is 8b58711.

@llvmbot
Copy link
Member

llvmbot commented May 26, 2023

@llvm/issue-subscribers-backend-webassembly

@aheejin
Copy link
Member

aheejin commented Jun 8, 2023

Thanks for the report. I filed #63183 as a simplified version of this for future reference. I don't think the fix is trivial and will be ready soon, but will work on later. By the way I'm curious, which source code + frontend produced this code?

@SingleAccretion
Copy link
Contributor Author

By the way I'm curious, which source code + frontend produced this code?

I encountered this when adding support for WASM EH in NativeAOT-LLVM. The way we did codegen with C++ (aka Emscripten) EH naturally results in this kind of flow when translated directly into WASM EH due to the dispatch always going through a top-level one-per-method switch.

I ended up altering the flow to have one switch per (WASM) catch in dotnet/runtimelab#2291, both to avoid this bug and for code quality reasons.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:WebAssembly confirmed Verified by a second party
Projects
None yet
Development

No branches or pull requests

4 participants