<div dir="ltr">Hi,<br><br>  I have a small procedure that works without optimisations but<br>doesn't with opt -O3. It's written in Modula3 and tests the exception<br>handling of the language. M3 uses traditional setjmp - longjmp for exceptions. <br>In the example, both raised and finally should be true.<br><br>(*-------------------- Test RAISE in FINALLY with another RAISE on stack. ---*)<br><br>PROCEDURE P25 () =<br>  VAR raised := FALSE;  finally := FALSE;<br>  BEGIN<br>    TRY<br>      TRY<br>        raised := TRUE;<br>        RAISE E;<br>      FINALLY<br>        finally := TRUE;<br>        RAISE E;<br>      END;<br>      Test.check (FALSE); <*NOWARN*><br>    EXCEPT E =><br>      Test.checkM(raised," P25 raised");<br>      Test.checkM(finally," P25 finally");<br>    END;<br>  END P25;<br><br><br><br>Here's the generated IR - the finally block gets turned into a nested procedure<br>which I haven't shown, hence the display. And setjmp has the returns_twice attribute<br>which doesn't seem to make any difference.<br><br>define void @Main__P25() #0 !dbg !303 {<br>entry:<br>  %raised = alloca i8, align 1<br>  %finally = alloca i8, align 1<br>  %tmp.181 = alloca i8*, align 8<br>  %tmp.182 = alloca i8*, align 8<br>  %tmp.183 = alloca i64, align 8<br>  %tmp.184 = alloca %struct.2, align 8<br>  %tmp.185 = alloca %struct.3, align 8<br>  %__Display = alloca [1 x i8*]<br>  %__Display.i8pp = bitcast [1 x i8*]* %__Display to i8**<br>  %__NewDisplaySlot.i8pp = getelementptr inbounds i8*, i8** %__Display.i8pp, i64 0<br>  store i8* %finally, i8** %__NewDisplaySlot.i8pp<br>  br label %second, !dbg !304<br><br>second:                                           ; preds = %entry<br>  call void @llvm.dbg.declare(metadata i8* %raised, metadata !305, metadata !DIExpression()), !dbg !307<br>  call void @llvm.dbg.declare(metadata i8* %finally, metadata !308, metadata !DIExpression()), !dbg !307<br>  store i8 0, i8* %finally, align 1, !dbg !309<br>  store i8 0, i8* %raised, align 1, !dbg !309<br>  store i8 0, i8* %finally, align 1, !dbg !309<br>  %v.58 = load i64, i64* @m3_jmpbuf_size, align 8, !dbg !309<br>  store i64 %v.58, i64* %tmp.183, align 8, !dbg !309<br>  %v.183 = load i64, i64* %tmp.183, align 8, !dbg !309<br>  %umul = mul nuw i64 2, %v.183, !dbg !309<br>  %jmpbuf_size = alloca i8, i64 %umul, !dbg !309<br>  store i8* %jmpbuf_size, i8** %tmp.181, align 8, !dbg !309<br>  %v.181 = load i8*, i8** %tmp.181, align 8, !dbg !309<br>  %loophole-addr_word = ptrtoint i8* %v.181 to i64, !dbg !309<br>  %v.1831 = load i64, i64* %tmp.183, align 8, !dbg !309<br>  %uadd = add nuw i64 %loophole-addr_word, %v.1831, !dbg !309<br>  %loophole-addr_word2 = inttoptr i64 %uadd to i8*, !dbg !309<br>  store i8* %loophole-addr_word2, i8** %tmp.182, align 8, !dbg !309<br>  br label %label_121, !dbg !310<br><br>label_121:                                        ; preds = %second<br>  %store_base.i8p = bitcast %struct.2* %tmp.184 to i8*, !dbg !310<br>  %store_dest.i8p = getelementptr inbounds i8, i8* %store_base.i8p, i64 16, !dbg !310<br>  %store_dest = bitcast i8* %store_dest.i8p to i8**, !dbg !310<br>  store i8* getelementptr inbounds (i8, i8* bitcast (%M_Const_struct* @M_Const to i8*), i64 136), i8** %store_dest, align 8, !dbg !310<br>  %store_base.i8p3 = bitcast %struct.2* %tmp.184 to i8*, !dbg !310<br>  %store_dest.i8p4 = getelementptr inbounds i8, i8* %store_base.i8p3, i64 8, !dbg !310<br>  %store_dest5 = bitcast i8* %store_dest.i8p4 to i64*, !dbg !310<br>  store i64 0, i64* %store_dest5, align 8, !dbg !310<br>  %pop_toadr = bitcast %struct.2* %tmp.184 to i8*, !dbg !310<br>  call void @RTHooks__PushEFrame(i8* %pop_toadr), !dbg !310<br>  %v.1816 = load i8*, i8** %tmp.181, align 8, !dbg !310<br>  %store_base.i8p7 = bitcast %struct.2* %tmp.184 to i8*, !dbg !310<br>  %store_dest.i8p8 = getelementptr inbounds i8, i8* %store_base.i8p7, i64 96, !dbg !310<br>  %store_dest9 = bitcast i8* %store_dest.i8p8 to i8**, !dbg !310<br>  store i8* %v.1816, i8** %store_dest9, align 8, !dbg !310<br>  %v.18110 = load i8*, i8** %tmp.181, align 8, !dbg !310<br>  %result = call i64 @_setjmp(i8* %v.18110), !dbg !310<br>  %icmp = icmp ne i64 %result, 0, !dbg !310<br>  br i1 %icmp, label %if_122, label %else_122, !dbg !310<br><br>else_122:                                         ; preds = %label_121<br>  %store_base.i8p11 = bitcast %struct.3* %tmp.185 to i8*, !dbg !311<br>  %store_dest.i8p12 = getelementptr inbounds i8, i8* %store_base.i8p11, i64 16, !dbg !311<br>  %store_dest13 = bitcast i8* %store_dest.i8p12 to i8**, !dbg !311<br>  store i8* bitcast (void (i8*)* @Main_M3_LINE_451 to i8*), i8** %store_dest13, align 8, !dbg !311<br>  %__static_link_from_display = bitcast i8** %__Display.i8pp to i8*, !dbg !311<br>  %store_base.i8p14 = bitcast %struct.3* %tmp.185 to i8*, !dbg !311<br>  %store_dest.i8p15 = getelementptr inbounds i8, i8* %store_base.i8p14, i64 24, !dbg !311<br>  %store_dest16 = bitcast i8* %store_dest.i8p15 to i8**, !dbg !311<br>  store i8* %__static_link_from_display, i8** %store_dest16, align 8, !dbg !311<br>  br label %label_123, !dbg !311<br><br>label_123:                                        ; preds = %else_122<br>  %store_base.i8p17 = bitcast %struct.3* %tmp.185 to i8*, !dbg !311<br>  %store_dest.i8p18 = getelementptr inbounds i8, i8* %store_base.i8p17, i64 8, !dbg !311<br>  %store_dest19 = bitcast i8* %store_dest.i8p18 to i64*, !dbg !311<br>  store i64 3, i64* %store_dest19, align 8, !dbg !311<br>  %pop_toadr20 = bitcast %struct.3* %tmp.185 to i8*, !dbg !311<br>  call void @RTHooks__PushEFrame(i8* %pop_toadr20), !dbg !311<br>  store i8 1, i8* %raised, align 1, !dbg !312<br>  call void @RTHooks__Raise(i8* bitcast (%M_Const_struct* @M_Const to i8*), i8* null, i8* bitcast (%M_Main_struct* @M_Main to i8*), i64 449), !dbg !313<br>  br label %label_124, !dbg !313<br><br>label_124:                                        ; preds = %label_123<br>  br label %if_122, !dbg !314<br><br>if_122:                                           ; preds = %label_124, %label_121<br>  %v.27 = load i8, i8* %raised, align 1, !dbg !315<br>  %zext = zext i8 %v.27 to i64, !dbg !315<br>  %pop_trunc = trunc i64 %zext to i8, !dbg !315<br>  call void @Test__checkM(i8 %pop_trunc, i8* getelementptr inbounds (i8, i8* bitcast (%M_Const_struct* @M_Const to i8*), i64 280)), !dbg !315<br><br><br>  Here is a portion of the optimised IR. <br><br>...<br><br>  %result = call i64 @_setjmp(i8* nonnull %jmpbuf_size), !dbg !465<br>  %icmp = icmp eq i64 %result, 0, !dbg !465<br>  br i1 %icmp, label %else_122, label %if_122, !dbg !465<br><br>else_122:                                         ; preds = %entry<br>  %tmp.1853.sub = getelementptr inbounds [40 x i8], [40 x i8]* %tmp.1853, i64 0, i64 0<br>  %store_dest.i8p12 = getelementptr inbounds [40 x i8], [40 x i8]* %tmp.1853, i64 0, i64 16, !dbg !466<br>  %store_dest13 = bitcast i8* %store_dest.i8p12 to i8**, !dbg !466<br>  store i8* bitcast (void (i8*)* @Main_M3_LINE_451 to i8*), i8** %store_dest13, align 8, !dbg !466<br>  %store_dest.i8p15 = getelementptr inbounds [40 x i8], [40 x i8]* %tmp.1853, i64 0, i64 24, !dbg !466<br>  %0 = bitcast i8* %store_dest.i8p15 to i8***, !dbg !466<br>  store i8** %__Display, i8*** %0, align 8, !dbg !466<br>  %store_dest.i8p18 = getelementptr inbounds [40 x i8], [40 x i8]* %tmp.1853, i64 0, i64 8, !dbg !466<br>  %store_dest19 = bitcast i8* %store_dest.i8p18 to i64*, !dbg !466<br>  store i64 3, i64* %store_dest19, align 8, !dbg !466<br>  call void @RTHooks__PushEFrame(i8* nonnull %tmp.1853.sub), !dbg !466<br>  call void @llvm.dbg.value(metadata i8 1, metadata !460, metadata !DIExpression()), !dbg !462<br>  call void @RTHooks__Raise(i8* bitcast (%M_Const_struct* @M_Const to i8*), i8* null, i8* bitcast (%M_Main_struct* @M_Main to i8*), i64 449), !dbg !467<br>  br label %if_122, !dbg !468<br><br>if_122:                                           ; preds = %entry, %else_122<br>  %raised.0 = phi i8 [ 0, %entry ], [ 1, %else_122 ], !dbg !462<br>  call void @llvm.dbg.value(metadata i8 %raised.0, metadata !460, metadata !DIExpression()), !dbg !462<br>  call void @Test__checkM(i8 %raised.0, i8* bitcast (i8** getelementptr inbounds (%M_Const_struct, %M_Const_struct* @M_Const, i64 0, i32 39) to i8*)), !dbg !469<br><br>...<br><br>and the relevant bit of assembly in X86_64<br><br>        leaq    M_Const(%rip), %rdi<br>        leaq    M_Main(%rip), %rdx<br>        movl    $449, %ecx              # imm = 0x1C1<br>        xorl    %esi, %esi<br>        callq   RTHooks__Raise@PLT<br>        movb    $1, %al                    <--------Note setting raised to true after longjmp<br>.Ltmp272:<br>.LBB47_3:                               # %if_122<br>        #DEBUG_VALUE: P25:finally <- 0<br>        #DEBUG_VALUE: P25:raised <- $al<br>        .loc    1 457 0                 # Main.m3:457:0<br>        movzbl  %al, %edi<br>        leaq    M_Const+280(%rip), %rsi<br>        callq   Test__checkM@PLT<br><br><br><br>  The raised alloca has been optimised away. The store to raised of 1 before the RTHooks__Raise has been eliminated which means it remains false until the phi after the Raise.<br>I guess because it's impossible to know that RTHooks__Raise eventually calls longjmp.<br><br>After a bit of digging, I found that volatile on loads and stores could fix the problem.<br>Setting it on loads alone doesn't help but setting all stores in the procedure to volatile<br>does the trick.<br><br>I'm wondering if this is the right stick to use and whether it's too big. A try except<br>block might be a small percentage of a function and I might be losing optimisations<br>elsewhere.<br><br>Thanks Peter<br></div>