Home | History | Annotate | Download | only in Inline
      1 ; RUN: opt -inline -S %s | FileCheck %s
      2 
      3 declare void @g()
      4 
      5 
      6 ;;; Test with a call in a funclet that needs to remain a call
      7 ;;; when inlined because the funclet doesn't unwind to caller.
      8 ;;; CHECK-LABEL: define void @test1(
      9 define void @test1() personality void ()* @g {
     10 entry:
     11 ; CHECK-NEXT: entry:
     12   invoke void @test1_inlinee()
     13     to label %exit unwind label %cleanup
     14 cleanup:
     15   %pad = cleanuppad within none []
     16   call void @g() [ "funclet"(token %pad) ]
     17   cleanupret from %pad unwind to caller
     18 exit:
     19   ret void
     20 }
     21 
     22 define void @test1_inlinee() alwaysinline personality void ()* @g {
     23 entry:
     24   invoke void @g()
     25     to label %exit unwind label %cleanup.inner
     26 ; CHECK-NEXT:  invoke void @g()
     27 ; CHECK-NEXT:    unwind label %[[cleanup_inner:.+]]
     28 
     29 cleanup.inner:
     30   %pad.inner = cleanuppad within none []
     31   call void @g() [ "funclet"(token %pad.inner) ]
     32   cleanupret from %pad.inner unwind label %cleanup.outer
     33 ; CHECK: [[cleanup_inner]]:
     34 ; The call here needs to remain a call becuase pad.inner has a cleanupret
     35 ; that stays within the inlinee.
     36 ; CHECK-NEXT:  %[[pad_inner:[^ ]+]] = cleanuppad within none
     37 ; CHECK-NEXT:  call void @g() [ "funclet"(token %[[pad_inner]]) ]
     38 ; CHECK-NEXT:  cleanupret from %[[pad_inner]] unwind label %[[cleanup_outer:.+]]
     39 
     40 cleanup.outer:
     41   %pad.outer = cleanuppad within none []
     42   call void @g() [ "funclet"(token %pad.outer) ]
     43   cleanupret from %pad.outer unwind to caller
     44 ; CHECK: [[cleanup_outer]]:
     45 ; The call and cleanupret here need to be redirected to caller cleanup
     46 ; CHECK-NEXT: %[[pad_outer:[^ ]+]] = cleanuppad within none
     47 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[pad_outer]]) ]
     48 ; CHECK-NEXT:   unwind label %cleanup
     49 ; CHECK: cleanupret from %[[pad_outer]] unwind label %cleanup{{$}}
     50 
     51 exit:
     52   ret void
     53 }
     54 
     55 
     56 
     57 ;;; Test with an "unwind to caller" catchswitch in a parent funclet
     58 ;;; that needs to remain "unwind to caller" because the parent
     59 ;;; doesn't unwind to caller.
     60 ;;; CHECK-LABEL: define void @test2(
     61 define void @test2() personality void ()* @g {
     62 entry:
     63 ; CHECK-NEXT: entry:
     64   invoke void @test2_inlinee()
     65     to label %exit unwind label %cleanup
     66 cleanup:
     67   %pad = cleanuppad within none []
     68   call void @g() [ "funclet"(token %pad) ]
     69   cleanupret from %pad unwind to caller
     70 exit:
     71   ret void
     72 }
     73 
     74 define void @test2_inlinee() alwaysinline personality void ()* @g {
     75 entry:
     76   invoke void @g()
     77     to label %exit unwind label %cleanup1
     78 ; CHECK-NEXT:   invoke void @g()
     79 ; CHECK-NEXT:     unwind label %[[cleanup1:.+]]
     80 
     81 cleanup1:
     82   %outer = cleanuppad within none []
     83   invoke void @g() [ "funclet"(token %outer) ]
     84     to label %ret1 unwind label %catchswitch
     85 ; CHECK: [[cleanup1]]:
     86 ; CHECK-NEXT: %[[outer:[^ ]+]] = cleanuppad within none
     87 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[outer]]) ]
     88 ; CHECK-NEXT:   unwind label %[[catchswitch:.+]]
     89 
     90 catchswitch:
     91   %cs = catchswitch within %outer [label %catch] unwind to caller
     92 ; CHECK: [[catchswitch]]:
     93 ; The catchswitch here needs to remain "unwind to caller" since %outer
     94 ; has a cleanupret that remains within the inlinee.
     95 ; CHECK-NEXT: %[[cs:[^ ]+]] = catchswitch within %[[outer]] [label %[[catch:.+]]] unwind to caller
     96 
     97 catch:
     98   %inner = catchpad within %cs []
     99   call void @g() [ "funclet"(token %inner) ]
    100   catchret from %inner to label %ret1
    101 ; CHECK: [[catch]]:
    102 ; The call here needs to remain a call since it too is within %outer
    103 ; CHECK:   %[[inner:[^ ]+]] = catchpad within %[[cs]]
    104 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[inner]]) ]
    105 
    106 ret1:
    107   cleanupret from %outer unwind label %cleanup2
    108 ; CHECK: cleanupret from %[[outer]] unwind label %[[cleanup2:.+]]
    109 
    110 cleanup2:
    111   %later = cleanuppad within none []
    112   cleanupret from %later unwind to caller
    113 ; CHECK: [[cleanup2]]:
    114 ; The cleanupret here needs to get redirected to the caller cleanup
    115 ; CHECK-NEXT: %[[later:[^ ]+]] = cleanuppad within none
    116 ; CHECK-NEXT: cleanupret from %[[later]] unwind label %cleanup{{$}}
    117 
    118 exit:
    119   ret void
    120 }
    121 
    122 
    123 ;;; Test with a call in a cleanup that has no definitive unwind
    124 ;;; destination, that must be rewritten to an invoke.
    125 ;;; CHECK-LABEL: define void @test3(
    126 define void @test3() personality void ()* @g {
    127 entry:
    128 ; CHECK-NEXT: entry:
    129   invoke void @test3_inlinee()
    130     to label %exit unwind label %cleanup
    131 cleanup:
    132   %pad = cleanuppad within none []
    133   call void @g() [ "funclet"(token %pad) ]
    134   cleanupret from %pad unwind to caller
    135 exit:
    136   ret void
    137 }
    138 
    139 define void @test3_inlinee() alwaysinline personality void ()* @g {
    140 entry:
    141   invoke void @g()
    142     to label %exit unwind label %cleanup
    143 ; CHECK-NEXT:  invoke void @g()
    144 ; CHECK-NEXT:    unwind label %[[cleanup:.+]]
    145 
    146 cleanup:
    147   %pad = cleanuppad within none []
    148   call void @g() [ "funclet"(token %pad) ]
    149   unreachable
    150 ; CHECK: [[cleanup]]:
    151 ; The call must be rewritten to an invoke targeting the caller cleanup
    152 ; because it may well unwind to there.
    153 ; CHECK-NEXT: %[[pad:[^ ]+]] = cleanuppad within none
    154 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[pad]]) ]
    155 ; CHECK-NEXT:   unwind label %cleanup{{$}}
    156 
    157 exit:
    158   ret void
    159 }
    160 
    161 
    162 ;;; Test with a catchswitch in a cleanup that has no definitive
    163 ;;; unwind destination, that must be rewritten to unwind to the
    164 ;;; inlined invoke's unwind dest
    165 ;;; CHECK-LABEL: define void @test4(
    166 define void @test4() personality void ()* @g {
    167 entry:
    168 ; CHECK-NEXT: entry:
    169   invoke void @test4_inlinee()
    170     to label %exit unwind label %cleanup
    171 cleanup:
    172   %pad = cleanuppad within none []
    173   call void @g() [ "funclet"(token %pad) ]
    174   cleanupret from %pad unwind to caller
    175 exit:
    176   ret void
    177 }
    178 
    179 define void @test4_inlinee() alwaysinline personality void ()* @g {
    180 entry:
    181   invoke void @g()
    182     to label %exit unwind label %cleanup
    183 ; CHECK-NEXT: invoke void @g()
    184 ; CHECK-NEXT:   unwind label %[[cleanup:.+]]
    185 
    186 cleanup:
    187   %clean = cleanuppad within none []
    188   invoke void @g() [ "funclet"(token %clean) ]
    189     to label %unreachable unwind label %dispatch
    190 ; CHECK: [[cleanup]]:
    191 ; CHECK-NEXT: %[[clean:[^ ]+]] = cleanuppad within none
    192 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[clean]]) ]
    193 ; CHECK-NEXT:   unwind label %[[dispatch:.+]]
    194 
    195 dispatch:
    196   %cs = catchswitch within %clean [label %catch] unwind to caller
    197 ; CHECK: [[dispatch]]:
    198 ; The catchswitch must be rewritten to unwind to %cleanup in the caller
    199 ; because it may well unwind to there.
    200 ; CHECK-NEXT: %[[cs:[^ ]+]] = catchswitch within %[[clean]] [label %[[catch:.+]]] unwind label %cleanup{{$}}
    201 
    202 catch:
    203   catchpad within %cs []
    204   br label %unreachable
    205 unreachable:
    206   unreachable
    207 exit:
    208   ret void
    209 }
    210 
    211 
    212 ;;; Test with multiple levels of nesting, and unwind dests
    213 ;;; that need to be inferred from ancestors, descendants,
    214 ;;; and cousins.
    215 ;;; CHECK-LABEL: define void @test5(
    216 define void @test5() personality void ()* @g {
    217 entry:
    218 ; CHECK-NEXT: entry:
    219   invoke void @test5_inlinee()
    220     to label %exit unwind label %cleanup
    221 cleanup:
    222   %pad = cleanuppad within none []
    223   call void @g() [ "funclet"(token %pad) ]
    224   cleanupret from %pad unwind to caller
    225 exit:
    226   ret void
    227 }
    228 
    229 define void @test5_inlinee() alwaysinline personality void ()* @g {
    230 entry:
    231   invoke void @g()
    232     to label %cont unwind label %noinfo.root
    233 ; CHECK-NEXT: invoke void @g()
    234 ; CHECK-NEXT:   to label %[[cont:[^ ]+]] unwind label %[[noinfo_root:.+]]
    235 
    236 noinfo.root:
    237   %noinfo.root.pad = cleanuppad within none []
    238   call void @g() [ "funclet"(token %noinfo.root.pad) ]
    239   invoke void @g() [ "funclet"(token %noinfo.root.pad) ]
    240     to label %noinfo.root.cont unwind label %noinfo.left
    241 ; CHECK: [[noinfo_root]]:
    242 ; Nothing under "noinfo.root" has a definitive unwind destination, so
    243 ; we must assume all of it may actually unwind, and redirect unwinds
    244 ; to the cleanup in the caller.
    245 ; CHECK-NEXT: %[[noinfo_root_pad:[^ ]+]] = cleanuppad within none []
    246 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
    247 ; CHECK-NEXT:   to label %[[next:[^ ]+]] unwind label %cleanup{{$}}
    248 ; CHECK: [[next]]:
    249 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
    250 ; CHECK-NEXT:   to label %[[noinfo_root_cont:[^ ]+]] unwind label %[[noinfo_left:.+]]
    251 
    252 noinfo.left:
    253   %noinfo.left.pad = cleanuppad within %noinfo.root.pad []
    254   invoke void @g() [ "funclet"(token %noinfo.left.pad) ]
    255     to label %unreachable unwind label %noinfo.left.child
    256 ; CHECK: [[noinfo_left]]:
    257 ; CHECK-NEXT: %[[noinfo_left_pad:[^ ]+]] = cleanuppad within %[[noinfo_root_pad]]
    258 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_left_pad]]) ]
    259 ; CHECK-NEXT:   unwind label %[[noinfo_left_child:.+]]
    260 
    261 noinfo.left.child:
    262   %noinfo.left.child.cs = catchswitch within %noinfo.left.pad [label %noinfo.left.child.catch] unwind to caller
    263 ; CHECK: [[noinfo_left_child]]:
    264 ; CHECK-NEXT: %[[noinfo_left_child_cs:[^ ]+]] = catchswitch within %[[noinfo_left_pad]] [label %[[noinfo_left_child_catch:[^ ]+]]] unwind label %cleanup{{$}}
    265 
    266 noinfo.left.child.catch:
    267   %noinfo.left.child.pad = catchpad within %noinfo.left.child.cs []
    268   call void @g() [ "funclet"(token %noinfo.left.child.pad) ]
    269   br label %unreachable
    270 ; CHECK: [[noinfo_left_child_catch]]:
    271 ; CHECK-NEXT: %[[noinfo_left_child_pad:[^ ]+]] = catchpad within %[[noinfo_left_child_cs]] []
    272 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_left_child_pad]]) ]
    273 ; CHECK-NEXT:   unwind label %cleanup{{$}}
    274 
    275 noinfo.root.cont:
    276   invoke void @g() [ "funclet"(token %noinfo.root.pad) ]
    277     to label %unreachable unwind label %noinfo.right
    278 ; CHECK: [[noinfo_root_cont]]:
    279 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
    280 ; CHECK-NEXT:   unwind label %[[noinfo_right:.+]]
    281 
    282 noinfo.right:
    283   %noinfo.right.cs = catchswitch within %noinfo.root.pad [label %noinfo.right.catch] unwind to caller
    284 ; CHECK: [[noinfo_right]]:
    285 ; CHECK-NEXT: %[[noinfo_right_cs:[^ ]+]] = catchswitch within %[[noinfo_root_pad]] [label %[[noinfo_right_catch:[^ ]+]]] unwind label %cleanup{{$}}
    286 
    287 noinfo.right.catch:
    288   %noinfo.right.pad = catchpad within %noinfo.right.cs []
    289   invoke void @g() [ "funclet"(token %noinfo.right.pad) ]
    290     to label %unreachable unwind label %noinfo.right.child
    291 ; CHECK: [[noinfo_right_catch]]:
    292 ; CHECK-NEXT: %[[noinfo_right_pad:[^ ]+]] = catchpad within %[[noinfo_right_cs]]
    293 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_right_pad]]) ]
    294 ; CHECK-NEXT:   unwind label %[[noinfo_right_child:.+]]
    295 
    296 noinfo.right.child:
    297   %noinfo.right.child.pad = cleanuppad within %noinfo.right.pad []
    298   call void @g() [ "funclet"(token %noinfo.right.child.pad) ]
    299   br label %unreachable
    300 ; CHECK: [[noinfo_right_child]]:
    301 ; CHECK-NEXT: %[[noinfo_right_child_pad:[^ ]+]] = cleanuppad within %[[noinfo_right_pad]]
    302 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_right_child_pad]]) ]
    303 ; CHECK-NEXT:   unwind label %cleanup{{$}}
    304 
    305 cont:
    306   invoke void @g()
    307     to label %exit unwind label %implicit.root
    308 ; CHECK: [[cont]]:
    309 ; CHECK-NEXT: invoke void @g()
    310 ; CHECK-NEXT:   unwind label %[[implicit_root:.+]]
    311 
    312 implicit.root:
    313   %implicit.root.pad = cleanuppad within none []
    314   call void @g() [ "funclet"(token %implicit.root.pad) ]
    315   invoke void @g() [ "funclet"(token %implicit.root.pad) ]
    316     to label %implicit.root.cont unwind label %implicit.left
    317 ; CHECK: [[implicit_root]]:
    318 ; There's an unwind edge to %internal in implicit.right, and we need to propagate that
    319 ; fact down to implicit.right.grandchild, up to implicit.root, and down to
    320 ; implicit.left.child.catch, leaving all calls and "unwind to caller" catchswitches
    321 ; alone to so they don't conflict with the unwind edge in implicit.right
    322 ; CHECK-NEXT: %[[implicit_root_pad:[^ ]+]] = cleanuppad within none
    323 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
    324 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
    325 ; CHECK-NEXT:   to label %[[implicit_root_cont:[^ ]+]] unwind label %[[implicit_left:.+]]
    326 
    327 implicit.left:
    328   %implicit.left.pad = cleanuppad within %implicit.root.pad []
    329   invoke void @g() [ "funclet"(token %implicit.left.pad) ]
    330     to label %unreachable unwind label %implicit.left.child
    331 ; CHECK: [[implicit_left]]:
    332 ; CHECK-NEXT: %[[implicit_left_pad:[^ ]+]] = cleanuppad within %[[implicit_root_pad:[^ ]+]]
    333 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_left_pad]]) ]
    334 ; CHECK-NEXT:   unwind label %[[implicit_left_child:.+]]
    335 
    336 implicit.left.child:
    337   %implicit.left.child.cs = catchswitch within %implicit.left.pad [label %implicit.left.child.catch] unwind to caller
    338 ; CHECK: [[implicit_left_child]]:
    339 ; CHECK-NEXT: %[[implicit_left_child_cs:[^ ]+]] = catchswitch within %[[implicit_left_pad]] [label %[[implicit_left_child_catch:[^ ]+]]] unwind to caller
    340 
    341 implicit.left.child.catch:
    342   %implicit.left.child.pad = catchpad within %implicit.left.child.cs []
    343   call void @g() [ "funclet"(token %implicit.left.child.pad) ]
    344   br label %unreachable
    345 ; CHECK: [[implicit_left_child_catch]]:
    346 ; CHECK-NEXT: %[[implicit_left_child_pad:[^ ]+]] = catchpad within %[[implicit_left_child_cs]]
    347 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_left_child_pad]]) ]
    348 
    349 implicit.root.cont:
    350   invoke void @g() [ "funclet"(token %implicit.root.pad) ]
    351     to label %unreachable unwind label %implicit.right
    352 ; CHECK: [[implicit_root_cont]]:
    353 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
    354 ; CHECK-NEXT:   unwind label %[[implicit_right:.+]]
    355 
    356 implicit.right:
    357   %implicit.right.cs = catchswitch within %implicit.root.pad [label %implicit.right.catch] unwind label %internal
    358 ; CHECK: [[implicit_right]]:
    359 ; This is the unwind edge (to %internal) whose existence needs to get propagated around the "implicit" tree
    360 ; CHECK-NEXT: %[[implicit_right_cs:[^ ]+]] = catchswitch within %[[implicit_root_pad]] [label %[[implicit_right_catch:[^ ]+]]] unwind label %[[internal:.+]]
    361 
    362 implicit.right.catch:
    363   %implicit.right.pad = catchpad within %implicit.right.cs []
    364   invoke void @g() [ "funclet"(token %implicit.right.pad) ]
    365     to label %unreachable unwind label %implicit.right.child
    366 ; CHECK: [[implicit_right_catch]]:
    367 ; CHECK-NEXT: %[[implicit_right_pad:[^ ]+]] = catchpad within %[[implicit_right_cs]]
    368 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_right_pad]]) ]
    369 ; CHECK-NEXT:   unwind label %[[implicit_right_child:.+]]
    370 
    371 implicit.right.child:
    372   %implicit.right.child.pad = cleanuppad within %implicit.right.pad []
    373   invoke void @g() [ "funclet"(token %implicit.right.child.pad) ]
    374     to label %unreachable unwind label %implicit.right.grandchild
    375 ; CHECK: [[implicit_right_child]]:
    376 ; CHECK-NEXT: %[[implicit_right_child_pad:[^ ]+]] = cleanuppad within %[[implicit_right_pad]]
    377 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_right_child_pad]]) ]
    378 ; CHECK-NEXT:   unwind label %[[implicit_right_grandchild:.+]]
    379 
    380 implicit.right.grandchild:
    381   %implicit.right.grandchild.cs = catchswitch within %implicit.right.child.pad [label %implicit.right.grandchild.catch] unwind to caller
    382 ; CHECK: [[implicit_right_grandchild]]:
    383 ; CHECK-NEXT: %[[implicit_right_grandchild_cs:[^ ]+]] = catchswitch within %[[implicit_right_child_pad]] [label %[[implicit_right_grandchild_catch:[^ ]+]]] unwind to caller
    384 
    385 implicit.right.grandchild.catch:
    386   %implicit.right.grandhcild.pad = catchpad within %implicit.right.grandchild.cs []
    387   call void @g() [ "funclet"(token %implicit.right.grandhcild.pad) ]
    388   br label %unreachable
    389 ; CHECK: [[implicit_right_grandchild_catch]]:
    390 ; CHECK-NEXT: %[[implicit_right_grandhcild_pad:[^ ]+]] = catchpad within %[[implicit_right_grandchild_cs]]
    391 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_right_grandhcild_pad]]) ]
    392 
    393 internal:
    394   %internal.pad = cleanuppad within none []
    395   call void @g() [ "funclet"(token %internal.pad) ]
    396   cleanupret from %internal.pad unwind to caller
    397 ; CHECK: [[internal]]:
    398 ; internal is a cleanup with a "return to caller" cleanuppad; that needs to get redirected
    399 ; to %cleanup in the caller, and the call needs to get similarly rewritten to an invoke.
    400 ; CHECK-NEXT: %[[internal_pad:[^ ]+]] = cleanuppad within none
    401 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %internal.pad.i) ]
    402 ; CHECK-NEXT:   to label %[[next:[^ ]+]] unwind label %cleanup{{$}}
    403 ; CHECK: [[next]]:
    404 ; CHECK-NEXT: cleanupret from %[[internal_pad]] unwind label %cleanup{{$}}
    405 
    406 unreachable:
    407   unreachable
    408 exit:
    409   ret void
    410 }
    411 
    412 
    413 declare void @ProcessCLRException()
    414 
    415 ; Make sure the logic doesn't get tripped up when the inlined invoke is
    416 ; itself within a funclet in the caller.
    417 ; CHECK-LABEL: define void @test6(
    418 define void @test6() personality void ()* @ProcessCLRException {
    419 entry:
    420   invoke void @g()
    421     to label %exit unwind label %callsite_parent
    422 callsite_parent:
    423   %callsite_parent.pad = cleanuppad within none []
    424 ; CHECK: %callsite_parent.pad = cleanuppad within none
    425   invoke void @test6_inlinee() [ "funclet"(token %callsite_parent.pad) ]
    426     to label %ret unwind label %cleanup
    427 ret:
    428   cleanupret from %callsite_parent.pad unwind label %cleanup
    429 cleanup:
    430   %pad = cleanuppad within none []
    431   call void @g() [ "funclet"(token %pad) ]
    432   cleanupret from %pad unwind to caller
    433 exit:
    434   ret void
    435 }
    436 
    437 define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
    438 entry:
    439   invoke void @g()
    440     to label %exit unwind label %inlinee_cleanup
    441 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %callsite_parent.pad) ]
    442 ; CHECK-NEXT:   unwind label %[[inlinee_cleanup:.+]]
    443 
    444 inlinee_cleanup:
    445   %inlinee.pad = cleanuppad within none []
    446   call void @g() [ "funclet"(token %inlinee.pad) ]
    447   unreachable
    448 ; CHECK: [[inlinee_cleanup]]:
    449 ; CHECK-NEXT: %[[inlinee_pad:[^ ]+]] = cleanuppad within %callsite_parent.pad
    450 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[inlinee_pad]]) ]
    451 ; CHECK-NEXT:   unwind label %cleanup{{$}}
    452 
    453 exit:
    454   ret void
    455 }
    456