Index: lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp +++ lib/Target/WebAssembly/WebAssemblyCFGStackify.cpp @@ -458,12 +458,11 @@ /// the function, because it's never reached. However, in WebAssembly, blocks /// that end at the function end need to have a return type signature that /// matches the function signature, even though it's unreachable. This function -/// checks for such cases and fixes up the signatures. +/// checks for such cases and inserts an explicit unreachable. static void FixEndsAtEndOfFunction( MachineFunction &MF, const WebAssemblyFunctionInfo &MFI, - DenseMap &BlockTops, - DenseMap &LoopTops) { + const WebAssemblyInstrInfo &TII) { assert(MFI.getResults().size() <= 1); if (MFI.getResults().empty()) @@ -488,15 +487,14 @@ for (MachineInstr &MI : reverse(MBB)) { if (MI.isPosition() || MI.isDebugValue()) continue; - if (MI.getOpcode() == WebAssembly::END_BLOCK) { - BlockTops[&MI]->getOperand(0).setImm(int32_t(retType)); - continue; - } - if (MI.getOpcode() == WebAssembly::END_LOOP) { - LoopTops[&MI]->getOperand(0).setImm(int32_t(retType)); - continue; + + if (MI.getOpcode() == WebAssembly::END_BLOCK || + MI.getOpcode() == WebAssembly::END_LOOP) { + const MCInstrDesc &Desc = TII.get(WebAssembly::UNREACHABLE); + BuildMI(&MBB, MI.getDebugLoc(), Desc); } - // Something other than an `end`. We're done. + + // Only need to consider the first non-debug machineinstr return; } } @@ -568,7 +566,7 @@ // Fix up block/loop signatures at the end of the function to conform to // WebAssembly's rules. - FixEndsAtEndOfFunction(MF, MFI, BlockTops, LoopTops); + FixEndsAtEndOfFunction(MF, MFI, TII); } bool WebAssemblyCFGStackify::runOnMachineFunction(MachineFunction &MF) { Index: test/CodeGen/WebAssembly/cfg-stackify.ll =================================================================== --- test/CodeGen/WebAssembly/cfg-stackify.ll +++ test/CodeGen/WebAssembly/cfg-stackify.ll @@ -275,17 +275,19 @@ ; CHECK-LABEL: minimal_loop: ; CHECK-NOT: br ; CHECK: .LBB7_1: -; CHECK: loop i32 +; CHECK: loop ; CHECK: i32.store 0($0), $pop{{[0-9]+}}{{$}} ; CHECK: br 0{{$}} ; CHECK: .LBB7_2: +; CHECK: unreachable{{$}} ; OPT-LABEL: minimal_loop: ; OPT-NOT: br ; OPT: .LBB7_1: -; OPT: loop i32 +; OPT: loop ; OPT: i32.store 0($0), $pop{{[0-9]+}}{{$}} ; OPT: br 0{{$}} ; OPT: .LBB7_2: +; OPT: unreachable{{$}} define i32 @minimal_loop(i32* %p) { entry: store volatile i32 0, i32* %p @@ -402,7 +404,7 @@ ; CHECK-LABEL: doublediamond_in_a_loop: ; CHECK: .LBB11_1: -; CHECK: loop i32{{$}} +; CHECK: loop {{$}} ; CHECK: block {{$}} ; CHECK: br_if 0, $0{{$}} ; CHECK: br 1{{$}} @@ -415,9 +417,10 @@ ; CHECK: br 0{{$}} ; CHECK: .LBB11_6: ; CHECK-NEXT: end_loop{{$}} +; CHECK-NEXT: unreachable{{$}} ; OPT-LABEL: doublediamond_in_a_loop: ; OPT: .LBB11_1: -; OPT: loop i32{{$}} +; OPT: loop {{$}} ; OPT: block {{$}} ; OPT: br_if 0, {{[^,]+}}{{$}} ; OPT: block {{$}} @@ -431,6 +434,7 @@ ; OPT: br 0{{$}} ; OPT: .LBB11_6: ; OPT-NEXT: end_loop{{$}} +; OPT-NEXT: unreachable{{$}} define i32 @doublediamond_in_a_loop(i32 %a, i32 %b, i32* %p) { entry: br label %header @@ -762,20 +766,22 @@ ; CHECK-LABEL: test8: ; CHECK: .LBB17_1: -; CHECK-NEXT: loop i32{{$}} +; CHECK-NEXT: loop {{$}} ; CHECK-NEXT: i32.const $push{{[^,]+}}, 0{{$}} ; CHECK-NEXT: br_if 0, {{[^,]+}}{{$}} ; CHECK-NEXT: br 0{{$}} ; CHECK-NEXT: .LBB17_2: ; CHECK-NEXT: end_loop{{$}} +; CHECK-NEXT: unreachable{{$}} ; OPT-LABEL: test8: ; OPT: .LBB17_1: -; OPT-NEXT: loop i32{{$}} +; OPT-NEXT: loop {{$}} ; OPT-NEXT: i32.const $push{{[^,]+}}, 0{{$}} ; OPT-NEXT: br_if 0, {{[^,]+}}{{$}} ; OPT-NEXT: br 0{{$}} ; OPT-NEXT: .LBB17_2: ; OPT-NEXT: end_loop{{$}} +; OPT-NEXT: unreachable{{$}} define i32 @test8() { bb: br label %bb1 @@ -1334,3 +1340,44 @@ bb15: ret void } + +; Test that nested loops and blocks typecheck correctly. + +; CHECK-LABEL: test16 +; CHECK: loop +; CHECK: loop +; CHECK-NEXT: block +; CHECK: end_block{{$}} +; CHECK: end_loop{{$}} +; CHECK-NEXT: end_loop{{$}} +; CHECK-NEXT: unreachable{{$}} +; CHECK-NEXT: .endfunc{{$}} +; OPT-LABEL: test16 +; OPT: loop +; OPT: loop +; OPT-NEXT: block +; OPT: end_block{{$}} +; OPT: end_loop{{$}} +; OPT-NEXT: end_loop{{$}} +; OPT-NEXT: unreachable{{$}} +; OPT-NEXT: .endfunc{{$}} +@test16global.ptr = global i32 0 +define hidden i32 @test16() { +entry: + br label %L1 + +L1: + %x = load i32, i32* @test16global.ptr + %y = add i32 %x, 1 + %cmp = icmp ne i32 %x, 2 + br i1 %cmp, label %L2, label %L3 + +L2: + store i32 %y, i32* @test16global.ptr + br i1 %cmp, label %L1, label %L3 + +L3: + %z = add i32 %y, 3 + store i32 %z, i32* @test16global.ptr + br label %L2 +}