This fixes the bug with multiple catch handlers at the same level. The test case looks like this:
void f() { try { may_throw(); } catch (int) { } catch (float) { } }
With this IR:
; Function Attrs: uwtable define void @"\01?f@@YAXXZ"() #0 { entry: %exn.slot = alloca i8* %ehselector.slot = alloca i32 %0 = alloca float, align 4 %1 = alloca i32, align 4 invoke void @"\01?may_throw@@YAXXZ"() to label %invoke.cont unwind label %lpad invoke.cont: ; preds = %entry br label %try.cont lpad: ; preds = %entry %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) catch %eh.HandlerMapEntry* @llvm.eh.handlermapentry.H catch %eh.HandlerMapEntry* @llvm.eh.handlermapentry.M %3 = extractvalue { i8*, i32 } %2, 0 store i8* %3, i8** %exn.slot %4 = extractvalue { i8*, i32 } %2, 1 store i32 %4, i32* %ehselector.slot br label %catch.dispatch catch.dispatch: ; preds = %lpad %sel = load i32, i32* %ehselector.slot %5 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry.H to i8*)) #3 %matches = icmp eq i32 %sel, %5 br i1 %matches, label %catch2, label %catch.fallthrough catch2: ; preds = %catch.dispatch %exn3 = load i8*, i8** %exn.slot %6 = bitcast i32* %1 to i8* call void @llvm.eh.begincatch(i8* %exn3, i8* %6) #3 call void @llvm.eh.endcatch() #3 br label %try.cont try.cont: ; preds = %catch2, %catch, %invoke.cont ret void catch.fallthrough: ; preds = %catch.dispatch %7 = call i32 @llvm.eh.typeid.for(i8* bitcast (%eh.HandlerMapEntry* @llvm.eh.handlermapentry.M to i8*)) #3 %matches1 = icmp eq i32 %sel, %7 br i1 %matches1, label %catch, label %eh.resume catch: ; preds = %catch.fallthrough %exn = load i8*, i8** %exn.slot %8 = bitcast float* %0 to i8* call void @llvm.eh.begincatch(i8* %exn, i8* %8) #3 call void @llvm.eh.endcatch() #3 br label %try.cont eh.resume: ; preds = %catch.fallthrough %exn4 = load i8*, i8** %exn.slot %sel5 = load i32, i32* %ehselector.slot %lpad.val = insertvalue { i8*, i32 } undef, i8* %exn4, 0 %lpad.val6 = insertvalue { i8*, i32 } %lpad.val, i32 %sel5, 1 resume { i8*, i32 } %lpad.val6 }
It's a small fix, but I thought it was worth pausing to talk about because the code involved seems potentially brittle.
The problem in this case was that the landing pad selector value was being loaded in one catch dispatch block but being referenced in two blocks. Neither the landing pad block mapping code nor the catch handling code were expecting this. For this case it was fairly simple to extend the code to recognize what was happening, but it raises a concern in my mind that the selector value, which we must be able to recognize for this pass to work properly, can be shuffled around through any manner of transformation. I can't see why it would be manipulated, beyond the basic optimizations to avoid storing and reloading it, but it can happen.
Do you think this needs to be more robust?