|
| 1 | +; RUN: opt -mtriple=x86_64-pc-windows-msvc -winehprepare -S -o - < %s | FileCheck %s |
| 2 | + |
| 3 | +; This test is based on the following code: |
| 4 | +; |
| 5 | +; struct SomeData { |
| 6 | +; int a; |
| 7 | +; int b; |
| 8 | +; }; |
| 9 | +; |
| 10 | +; void may_throw(); |
| 11 | +; void does_not_throw(int i); |
| 12 | +; void dump(int *, int, SomeData&); |
| 13 | +; |
| 14 | +; void test() { |
| 15 | +; int NumExceptions = 0; |
| 16 | +; int ExceptionVal[10]; |
| 17 | +; SomeData Data = { 0, 0 }; |
| 18 | +; |
| 19 | +; for (int i = 0; i < 10; ++i) { |
| 20 | +; try { |
| 21 | +; may_throw(); |
| 22 | +; Data.a += i; |
| 23 | +; } |
| 24 | +; catch (int e) { |
| 25 | +; ExceptionVal[NumExceptions] = e; |
| 26 | +; ++NumExceptions; |
| 27 | +; if (e == i) |
| 28 | +; Data.b += e; |
| 29 | +; else |
| 30 | +; Data.a += e; |
| 31 | +; } |
| 32 | +; does_not_throw(NumExceptions); |
| 33 | +; } |
| 34 | +; dump(ExceptionVal, NumExceptions, Data); |
| 35 | +; } |
| 36 | +; |
| 37 | +; Unlike the cppeh-frame-vars.ll test, this test was generated using -O2 |
| 38 | +; optimization, which results in non-alloca values being used in the |
| 39 | +; catch handler. |
| 40 | + |
| 41 | +; ModuleID = 'cppeh-frame-vars.cpp' |
| 42 | +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" |
| 43 | +target triple = "x86_64-pc-windows-msvc" |
| 44 | + |
| 45 | +%rtti.TypeDescriptor2 = type { i8**, i8*, [3 x i8] } |
| 46 | +%struct.SomeData = type { i32, i32 } |
| 47 | + |
| 48 | +$"\01??_R0H@8" = comdat any |
| 49 | + |
| 50 | +@"\01??_7type_info@@6B@" = external constant i8* |
| 51 | +@"\01??_R0H@8" = linkonce_odr global %rtti.TypeDescriptor2 { i8** @"\01??_7type_info@@6B@", i8* null, [3 x i8] c".H\00" }, comdat |
| 52 | + |
| 53 | +; This structure should be declared for the frame allocation block. |
| 54 | +; CHECK: %"struct.\01?test@@YAXXZ.ehdata" = type { i32, i8*, i32, [10 x i32], i32, i32*, i32* } |
| 55 | + |
| 56 | +; The function entry should be rewritten like this. |
| 57 | +; CHECK: define void @"\01?test@@YAXXZ"() #0 { |
| 58 | +; CHECK: entry: |
| 59 | +; CHECK: %frame.alloc = call i8* @llvm.frameallocate(i32 80) |
| 60 | +; CHECK: %eh.data = bitcast i8* %frame.alloc to %"struct.\01?test@@YAXXZ.ehdata"* |
| 61 | +; CHECK-NOT: %ExceptionVal = alloca [10 x i32], align 16 |
| 62 | +; CHECK: %NumExceptions.020.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 |
| 63 | +; CHECK: %i.019.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 |
| 64 | +; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 |
| 65 | +; CHECK: %Data = alloca i64, align 8 |
| 66 | +; CHECK: %tmpcast = bitcast i64* %Data to %struct.SomeData* |
| 67 | +; CHECK: %0 = bitcast [10 x i32]* %ExceptionVal to i8* |
| 68 | +; CHECK: call void @llvm.lifetime.start(i64 40, i8* %0) #1 |
| 69 | +; CHECK: store i64 0, i64* %Data, align 8 |
| 70 | +; CHECK: %a.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 |
| 71 | +; CHECK: %a = bitcast i64* %Data to i32* |
| 72 | +; CHECK: store i32* %a, i32** %a.reg2mem |
| 73 | +; CHECK: %b.reg2mem = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 |
| 74 | +; CHECK: %b = getelementptr inbounds %struct.SomeData* %tmpcast, i64 0, i32 1 |
| 75 | +; CHECK: store i32* %b, i32** %b.reg2mem |
| 76 | +; CHECK: store i32 0, i32* %NumExceptions.020.reg2mem |
| 77 | +; CHECK: store i32 0, i32* %i.019.reg2mem |
| 78 | +; CHECK: br label %for.body |
| 79 | + |
| 80 | +; Function Attrs: uwtable |
| 81 | +define void @"\01?test@@YAXXZ"() #0 { |
| 82 | +entry: |
| 83 | + %ExceptionVal = alloca [10 x i32], align 16 |
| 84 | + %Data = alloca i64, align 8 |
| 85 | + %tmpcast = bitcast i64* %Data to %struct.SomeData* |
| 86 | + %0 = bitcast [10 x i32]* %ExceptionVal to i8* |
| 87 | + call void @llvm.lifetime.start(i64 40, i8* %0) #1 |
| 88 | + store i64 0, i64* %Data, align 8 |
| 89 | + %a = bitcast i64* %Data to i32* |
| 90 | + %b = getelementptr inbounds %struct.SomeData* %tmpcast, i64 0, i32 1 |
| 91 | + br label %for.body |
| 92 | + |
| 93 | +; CHECK: for.body: |
| 94 | +; CHECK-NOT: %NumExceptions.020 = phi i32 [ 0, %entry ], [ %NumExceptions.1, %try.cont ] |
| 95 | +; CHECK-NOT: %i.019 = phi i32 [ 0, %entry ], [ %inc5, %try.cont ] |
| 96 | +; CHECK: %i.019.reload = load i32* %i.019.reg2mem |
| 97 | +; CHECK: %NumExceptions.020.reload = load i32* %NumExceptions.020.reg2mem |
| 98 | +for.body: ; preds = %entry, %try.cont |
| 99 | + %NumExceptions.020 = phi i32 [ 0, %entry ], [ %NumExceptions.1, %try.cont ] |
| 100 | + %i.019 = phi i32 [ 0, %entry ], [ %inc5, %try.cont ] |
| 101 | + invoke void @"\01?may_throw@@YAXXZ"() |
| 102 | + to label %invoke.cont unwind label %lpad |
| 103 | + |
| 104 | +; CHECK: invoke.cont: ; preds = %for.body |
| 105 | +; CHECK-NOT: %1 = load i32* %a, align 8, !tbaa !2 |
| 106 | +; CHECK-NOT: %add = add nsw i32 %1, %i.019 |
| 107 | +; CHECK-NOT: store i32 %add, i32* %a, align 8, !tbaa !2 |
| 108 | +; CHECK: %a.reload3 = load volatile i32** %a.reg2mem |
| 109 | +; CHECK: %1 = load i32* %a.reload3, align 8, !tbaa !2 |
| 110 | +; CHECK: %add = add nsw i32 %1, %i.019.reload |
| 111 | +; CHECK: %a.reload2 = load volatile i32** %a.reg2mem |
| 112 | +; CHECK: store i32 %add, i32* %a.reload2, align 8, !tbaa !2 |
| 113 | +; CHECK: br label %try.cont |
| 114 | +invoke.cont: ; preds = %for.body |
| 115 | + %1 = load i32* %a, align 8, !tbaa !2 |
| 116 | + %add = add nsw i32 %1, %i.019 |
| 117 | + store i32 %add, i32* %a, align 8, !tbaa !2 |
| 118 | + br label %try.cont |
| 119 | + |
| 120 | +lpad: ; preds = %for.body |
| 121 | + %2 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*) |
| 122 | + catch i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*) |
| 123 | + %3 = extractvalue { i8*, i32 } %2, 1 |
| 124 | + %4 = tail call i32 @llvm.eh.typeid.for(i8* bitcast (%rtti.TypeDescriptor2* @"\01??_R0H@8" to i8*)) #1 |
| 125 | + %matches = icmp eq i32 %3, %4 |
| 126 | + br i1 %matches, label %catch, label %eh.resume |
| 127 | + |
| 128 | +catch: ; preds = %lpad |
| 129 | + %5 = extractvalue { i8*, i32 } %2, 0 |
| 130 | + %6 = tail call i8* @llvm.eh.begincatch(i8* %5) #1 |
| 131 | + %7 = bitcast i8* %6 to i32* |
| 132 | + %8 = load i32* %7, align 4, !tbaa !7 |
| 133 | + %idxprom = sext i32 %NumExceptions.020 to i64 |
| 134 | + %arrayidx = getelementptr inbounds [10 x i32]* %ExceptionVal, i64 0, i64 %idxprom |
| 135 | + store i32 %8, i32* %arrayidx, align 4, !tbaa !7 |
| 136 | + %inc = add nsw i32 %NumExceptions.020, 1 |
| 137 | + %cmp1 = icmp eq i32 %8, %i.019 |
| 138 | + br i1 %cmp1, label %if.then, label %if.else |
| 139 | + |
| 140 | +if.then: ; preds = %catch |
| 141 | + %9 = load i32* %b, align 4, !tbaa !8 |
| 142 | + %add2 = add nsw i32 %9, %i.019 |
| 143 | + store i32 %add2, i32* %b, align 4, !tbaa !8 |
| 144 | + br label %if.end |
| 145 | + |
| 146 | +if.else: ; preds = %catch |
| 147 | + %10 = load i32* %a, align 8, !tbaa !2 |
| 148 | + %add4 = add nsw i32 %10, %8 |
| 149 | + store i32 %add4, i32* %a, align 8, !tbaa !2 |
| 150 | + br label %if.end |
| 151 | + |
| 152 | +if.end: ; preds = %if.else, %if.then |
| 153 | + tail call void @llvm.eh.endcatch() #1 |
| 154 | + br label %try.cont |
| 155 | + |
| 156 | +; CHECK: try.cont: ; preds = %if.end, %invoke.cont |
| 157 | +; CHECK-NOT: %NumExceptions.1 = phi i32 [ %NumExceptions.020, %invoke.cont ], [ %inc, %if.end ] |
| 158 | +; CHECK: %NumExceptions.1 = phi i32 [ %NumExceptions.020.reload, %invoke.cont ], [ %inc, %if.end ] |
| 159 | +; CHECK: tail call void @"\01?does_not_throw@@YAXH@Z"(i32 %NumExceptions.1) |
| 160 | +; CHECK-NOT: %inc5 = add nuw nsw i32 %i.019, 1 |
| 161 | +; CHECK: %inc5 = add nuw nsw i32 %i.019.reload, 1 |
| 162 | +; CHECK: %cmp = icmp slt i32 %inc5, 10 |
| 163 | +; CHECK: store i32 %NumExceptions.1, i32* %NumExceptions.020.reg2mem |
| 164 | +; CHECK: store i32 %inc5, i32* %i.019.reg2mem |
| 165 | +; CHECK: br i1 %cmp, label %for.body, label %for.end |
| 166 | + |
| 167 | +try.cont: ; preds = %if.end, %invoke.cont |
| 168 | + %NumExceptions.1 = phi i32 [ %NumExceptions.020, %invoke.cont ], [ %inc, %if.end ] |
| 169 | + tail call void @"\01?does_not_throw@@YAXH@Z"(i32 %NumExceptions.1) |
| 170 | + %inc5 = add nuw nsw i32 %i.019, 1 |
| 171 | + %cmp = icmp slt i32 %inc5, 10 |
| 172 | + br i1 %cmp, label %for.body, label %for.end |
| 173 | + |
| 174 | +for.end: ; preds = %try.cont |
| 175 | + %NumExceptions.1.lcssa = phi i32 [ %NumExceptions.1, %try.cont ] |
| 176 | + %arraydecay = getelementptr inbounds [10 x i32]* %ExceptionVal, i64 0, i64 0 |
| 177 | + call void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32* %arraydecay, i32 %NumExceptions.1.lcssa, %struct.SomeData* dereferenceable(8) %tmpcast) |
| 178 | + call void @llvm.lifetime.end(i64 40, i8* %0) #1 |
| 179 | + ret void |
| 180 | + |
| 181 | +eh.resume: ; preds = %lpad |
| 182 | + %.lcssa = phi { i8*, i32 } [ %2, %lpad ] |
| 183 | + resume { i8*, i32 } %.lcssa |
| 184 | +} |
| 185 | + |
| 186 | +; The following catch handler should be outlined. |
| 187 | +; CHECK: define i8* @"\01?test@@YAXXZ.catch"(i8*, i8*) { |
| 188 | +; CHECK: catch.entry: |
| 189 | +; CHECK: %eh.alloc = call i8* @llvm.framerecover(i8* bitcast (void ()* @"\01?test@@YAXXZ" to i8*), i8* %1) |
| 190 | +; CHECK: %eh.data = bitcast i8* %eh.alloc to %"struct.\01?test@@YAXXZ.ehdata"* |
| 191 | +; CHECK: %eh.obj.ptr = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 1 |
| 192 | +; CHECK: %eh.obj = load i8** %eh.obj.ptr |
| 193 | +; CHECK: %eh.temp.alloca = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 2 |
| 194 | +; CHECK: %NumExceptions.020.reload = load i32* %eh.temp.alloca |
| 195 | +; CHECK: %ExceptionVal = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 3 |
| 196 | +; CHECK: %eh.temp.alloca1 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 4 |
| 197 | +; CHECK: %i.019.reload = load i32* %eh.temp.alloca1 |
| 198 | +; CHECK: %eh.temp.alloca2 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 5 |
| 199 | +; CHECK: %a.reload = load i32** %eh.temp.alloca2 |
| 200 | +; CHECK: %eh.temp.alloca3 = getelementptr inbounds %"struct.\01?test@@YAXXZ.ehdata"* %eh.data, i32 0, i32 6 |
| 201 | +; CHECK: %b.reload = load i32** %eh.temp.alloca3 |
| 202 | +; CHECK: %2 = bitcast i8* %eh.obj to i32* |
| 203 | +; CHECK: %3 = load i32* %2, align 4, !tbaa !7 |
| 204 | +; CHECK: %idxprom = sext i32 %NumExceptions.020.reload to i64 |
| 205 | +; CHECK: %arrayidx = getelementptr inbounds [10 x i32]* %ExceptionVal, i64 0, i64 %idxprom |
| 206 | +; CHECK: store i32 %3, i32* %arrayidx, align 4, !tbaa !7 |
| 207 | +; CHECK: %inc = add nsw i32 %NumExceptions.020.reload, 1 |
| 208 | +; CHECK: %cmp1 = icmp eq i32 %3, %i.019.reload |
| 209 | +; CHECK: br i1 %cmp1, label %if.then, label %if.else |
| 210 | +; |
| 211 | +; CHECK: if.then: ; preds = %catch.entry |
| 212 | +; CHECK: %4 = load i32* %b.reload, align 4, !tbaa !8 |
| 213 | +; CHECK: %add2 = add nsw i32 %4, %i.019.reload |
| 214 | +; CHECK: store i32 %add2, i32* %b.reload, align 4, !tbaa !8 |
| 215 | +; CHECK: br label %if.end |
| 216 | +; |
| 217 | +; CHECK: if.else: ; preds = %catch.entry |
| 218 | +; CHECK: %5 = load i32* %a.reload, align 8, !tbaa !2 |
| 219 | +; CHECK: %add4 = add nsw i32 %5, %3 |
| 220 | +; CHECK: store i32 %add4, i32* %a.reload, align 8, !tbaa !2 |
| 221 | +; CHECK: br label %if.end |
| 222 | +; |
| 223 | +; CHECK: if.end: ; preds = %if.else, %if.then |
| 224 | +; CHECK: ret i8* blockaddress(@"\01?test@@YAXXZ", %try.cont) |
| 225 | +; CHECK: } |
| 226 | + |
| 227 | +; Function Attrs: nounwind |
| 228 | +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 |
| 229 | + |
| 230 | +declare void @"\01?may_throw@@YAXXZ"() #2 |
| 231 | + |
| 232 | +declare i32 @__CxxFrameHandler3(...) |
| 233 | + |
| 234 | +; Function Attrs: nounwind readnone |
| 235 | +declare i32 @llvm.eh.typeid.for(i8*) #3 |
| 236 | + |
| 237 | +declare i8* @llvm.eh.begincatch(i8*) |
| 238 | + |
| 239 | +declare void @llvm.eh.endcatch() |
| 240 | + |
| 241 | +declare void @"\01?does_not_throw@@YAXH@Z"(i32) #2 |
| 242 | + |
| 243 | +declare void @"\01?dump@@YAXPEAHHAEAUSomeData@@@Z"(i32*, i32, %struct.SomeData* dereferenceable(8)) #2 |
| 244 | + |
| 245 | +; Function Attrs: nounwind |
| 246 | +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 |
| 247 | + |
| 248 | +attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } |
| 249 | +attributes #1 = { nounwind } |
| 250 | +attributes #2 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } |
| 251 | +attributes #3 = { nounwind readnone } |
| 252 | + |
| 253 | +!llvm.module.flags = !{!0} |
| 254 | +!llvm.ident = !{!1} |
| 255 | + |
| 256 | +!0 = !{i32 1, !"PIC Level", i32 2} |
| 257 | +!1 = !{!"clang version 3.7.0 (trunk 228868)"} |
| 258 | +!2 = !{!3, !4, i64 0} |
| 259 | +!3 = !{!"?AUSomeData@@", !4, i64 0, !4, i64 4} |
| 260 | +!4 = !{!"int", !5, i64 0} |
| 261 | +!5 = !{!"omnipotent char", !6, i64 0} |
| 262 | +!6 = !{!"Simple C/C++ TBAA"} |
| 263 | +!7 = !{!4, !4, i64 0} |
| 264 | +!8 = !{!3, !4, i64 4} |
0 commit comments