Tozer, Stephen via llvm-dev
2020-Feb-25 16:39 UTC
[llvm-dev] [RFC] Allowing debug intrinsics to reference multiple SSA Values
>As the person who has advocated for DW_OP_LLVM_arg(N) before, my main motivation was to resolve the ambiguity of constant DIExpressions: As a worst-case example: > >dbg.value(%undef, !DILocalVariable(x), DIExpression(DW_OP_constu, 42)) > >Is this undefined, or constant 42? > >But if we make dbg.value fully variadic with all parameters pushed to the stack ahead of time, we can distinguish between > >dbg.value(!DILocalVariable(x), DIExpression(DW_OP_constu, 42)) ; a constant >dbg.value(i32 42, !DILocalVariable(x), DIExpression()) ; the same constant dbg.value(%undef, !DILocalVariable(x), DIExpression(DW_OP_constu, 42)) ; undef + garbage leftover expression > >If we do it this way and allow 0-n SSA values then I'm in support of the push-first scheme.This seems like as good a case as any to go with the push-first approach, since it solves a meaningful problem that the alternative does not.>As a side note, updating dbg.values in salvageDebugInfo when we allow multiple SSA values per dbg.value will be interesting, but possible. We'll need to put the value we need to update at the top of the stack, prepend the salvage expression and then restore the order of arguments on the stack that the rest of the expression expects.The difficulty here is that in DWARF5, there is no operator that can move an argument up the stack; there are specific operators that rotate the top 2 or 3 elements, but outside of that the only thing you can do is copy an element to the top with DW_OP_pick. Of course we don't have to stick to actual DWARF operators by any means, but I don't think there's any way to eventually express an indexed swap/move operator in DWARF either: you can duplicate an element from anywhere on the stack to the top, but you can't delete the original element or move the top element back down again. Because of this, it is necessary (I believe) to use DW_OP_pick in the general case even if all the arguments are initially pushed onto the stack, due to the limits on how we can manipulate the stack. Of course there are many expressions that don't require DW_OP_pick to evaluate, but for consistency's sake I think it's best to use it instead of the swap or rotate operators whenever any of them is needed. Regardless of whether we push everything immediately or not, having an operator that pushes the SSA value at a given argument index onto the stack seems necessary - if we push elements initially then DW_OP_pick (or DW_OP_LLVM_pick), otherwise DW_OP_LLVM_register. In either of these cases, salvaging is simple: we simply append the salvage expression after the salvaged value's operator. It also translates cleanly to DWARF, since we can directly replace the operator in question with DW_OP_regval_type, using whichever register holds the value we're looking for as an argument, without needing to modify or even consider the rest of the expression at all.
Adrian Prantl via llvm-dev
2020-Feb-25 17:06 UTC
[llvm-dev] [RFC] Allowing debug intrinsics to reference multiple SSA Values
> On Feb 25, 2020, at 8:39 AM, Tozer, Stephen <stephen.tozer at sony.com> wrote: > >> As the person who has advocated for DW_OP_LLVM_arg(N) before, my main motivation was to resolve the ambiguity of constant DIExpressions: As a worst-case example: >> >> dbg.value(%undef, !DILocalVariable(x), DIExpression(DW_OP_constu, 42)) >> >> Is this undefined, or constant 42? >> >> But if we make dbg.value fully variadic with all parameters pushed to the stack ahead of time, we can distinguish between >> >> dbg.value(!DILocalVariable(x), DIExpression(DW_OP_constu, 42)) ; a constant >> dbg.value(i32 42, !DILocalVariable(x), DIExpression()) ; the same constant dbg.value(%undef, !DILocalVariable(x), DIExpression(DW_OP_constu, 42)) ; undef + garbage leftover expression >> >> If we do it this way and allow 0-n SSA values then I'm in support of the push-first scheme. > > This seems like as good a case as any to go with the push-first approach, since it solves a meaningful problem that the alternative does not. > >> As a side note, updating dbg.values in salvageDebugInfo when we allow multiple SSA values per dbg.value will be interesting, but possible. We'll need to put the value we need to update at the top of the stack, prepend the salvage expression and then restore the order of arguments on the stack that the rest of the expression expects. > > The difficulty here is that in DWARF5, there is no operator that can move an argument up the stack; there are specific operators that rotate the top 2 or 3 elements, but outside of that the only thing you can do is copy an element to the top with DW_OP_pick. Of course we don't have to stick to actual DWARF operators by any means, but I don't think there's any way to eventually express an indexed swap/move operator in DWARF either: you can duplicate an element from anywhere on the stack to the top, but you can't delete the original element or move the top element back down again. Because of this, it is necessary (I believe) to use DW_OP_pick in the general case even if all the arguments are initially pushed onto the stack, due to the limits on how we can manipulate the stack. Of course there are many expressions that don't require DW_OP_pick to evaluate, but for consistency's sake I think it's best to use it instead of the swap or rotate operators whenever any of them is needed. > > Regardless of whether we push everything immediately or not, having an operator that pushes the SSA value at a given argument index onto the stack seems necessary - if we push elements initially then DW_OP_pick (or DW_OP_LLVM_pick), otherwise DW_OP_LLVM_register. In either of these cases, salvaging is simple: we simply append the salvage expression after the salvaged value's operator. It also translates cleanly to DWARF, since we can directly replace the operator in question with DW_OP_regval_type, using whichever register holds the value we're looking for as an argument, without needing to modify or even consider the rest of the expression at all. >When we have <= 3 arguments (I don't want to call them registers because they may turn into constants or memory locations later on) we can use DW_OP_rot to move the one we need to adjust to the top and the DW_OP_rot the stack back into the original order. For 4-255 arguments, we need to add extra operations both to the beginning and the end of the expression, here an example for salvaging the 4th element from the top: r0 r1 r2 r3 ; stack before DW_OP_pick 0 r0 r1 r2 r3 r0 ; stack after pick 0 ... <adjustments> ; the salvage operations for r0 r0 r1 r2 r3 r0' DW_OP_pick 1 r0 r1 r2 r3 r0' r1 DW_OP_pick 2 r0 r1 r2 r3 r0' r1 r2 DW_OP_pick 3 r0 r1 r2 r3 r0' r1 r2 r3 <rest of expression> r0 r1 r2 r3 result DW_OP_swap r0 r1 r2 result r3 DW_OP_drop r0 r1 r2 result DW_OP_swap r0 r1 result r2 DW_OP_drop DW_OP_rot DW_OP_drop DW_OP_drop result It's possible to do with just DWARF expressions, but the resulting expression may get a little long. We could also just say we support up to 3 arguments and that would probably be fine. -- adrian
Tozer, Stephen via llvm-dev
2020-Feb-25 18:17 UTC
[llvm-dev] [RFC] Allowing debug intrinsics to reference multiple SSA Values
> When we have <= 3 arguments (I don't want to call them registers because they may turn into constants or memory locations later on) we can use DW_OP_rot to move the one we need to adjust to the top and the DW_OP_rot the stack back into the original order. For 4-255 arguments, we need to add extra operations both to the beginning and the end of the expression, here an example for salvaging the 4th element from the top:That example is how I'd imagine DW_OP_pick/DW_OP_LLVM_register to be used. Personally I'm of the opinion that although we can use DW_OP_rot for cases with arguments <= 3, it would be better to use DW_OP_pick in those cases. One reason is consistency; the other is that as you mentioned, the expressions can get rather long, and this gets worse if we start by using DW_OP_rot and then switch to DW_OP_pick. For example if we have (assuming the first arg is on top of the stack): %c = add %e, %f %b = div %d, 7 dv(!DIExpression(DW_OP_plus, DW_OP_mul), %a, %b, %c) ; Short and sweet dv(!DIExpression(DW_OP_pick, 2, DW_OP_pick, 1, DW_OP_pick, 0, DW_OP_plus, DW_OP_mul), %a, %b, %c) ; Verbose ; Salvage %b dv(!DIExpression(DW_OP_rot, DW_OP_constu, 7, DW_OP_div, DW_OP_rot, DW_OP_rot, DW_OP_plus, DW_OP_mul), %a, %d, %c) ; Suddenly a lot longer dv(!DIExpression(DW_OP_pick, 2, DW_OP_pick, 1, DW_OP_constu, 7, DW_OP_div, DW_OP_pick, 0, DW_OP_plus, DW_OP_mul), %a, %d, %c) ; Still longer, but not by as much ; Salvage %c dv(!DIExpression(DW_OP_pick, 2, DW_OP_pick, 3, DW_OP_plus, DW_OP_pick, 1, DW_OP_pick, 0, DW_OP_rot, DW_OP_constu, 7, DW_OP_div, DW_OP_rot, DW_OP_rot, DW_OP_plus, DW_OP_mul), %a, %d, %e, %f) ; Sudden conversion to use DW_OP_pick; now necessary to pick every argument, while still using DW_OP_rot dv(!DIExpression(DW_OP_pick, 2, DW_OP_pick, 3, DW_OP_plus, DW_OP_pick, 1, DW_OP_constu, 7, DW_OP_div, DW_OP_pick, 0, DW_OP_plus, DW_OP_mul), %a, %d, %e, %f) ; Now shorter, due to not containing DW_OP_rot We could avoid this sudden growth in expression length by having salvage debug info attempt to remove the use of DW_OP_rot, but this would make salvaging more complicated as it would need to cover the cases where we: 1. Salvage a simple expression that doesn't need rotating or picking: prepend salvage instructions. 2. Salvage a simple expression that now needs rotation: insert 3x DW_OP_rot, insert salvage instructions at some point in the middle. 3. Salvage an expression with rotation: determine where salvage instructions need to go and place them. 4. Salvage an expression with rotation that now needs picking: insert DW_OP_pick for each arg, move salvages from the middle of DW_OP_rot to DW_OP_pick, remove DW_OP_rot. 5. Salvage an expression with picking: append salvage instructions to the relevant DW_OP_pick operator. I think the best solution is the one where we keep the "always on the stack" behaviour, so that we can avoid using DW_OP_pick where it doesn't do anything useful (as in the first case in the example), while keeping salvageDebugInfo simple. ________________________________ From: aprantl at apple.com <aprantl at apple.com> on behalf of Adrian Prantl <aprantl at apple.com> Sent: 25 February 2020 17:06 To: Tozer, Stephen <stephen.tozer at sony.com> Cc: David Blaikie <dblaikie at gmail.com>; Jonas Devlieghere <jdevlieghere at apple.com>; Robinson, Paul <paul.robinson at sony.com>; Eric Christopher <echristo at gmail.com>; llvm-dev at lists.llvm.org <llvm-dev at lists.llvm.org> Subject: Re: [llvm-dev] [RFC] Allowing debug intrinsics to reference multiple SSA Values> On Feb 25, 2020, at 8:39 AM, Tozer, Stephen <stephen.tozer at sony.com> wrote: > >> As the person who has advocated for DW_OP_LLVM_arg(N) before, my main motivation was to resolve the ambiguity of constant DIExpressions: As a worst-case example: >> >> dbg.value(%undef, !DILocalVariable(x), DIExpression(DW_OP_constu, 42)) >> >> Is this undefined, or constant 42? >> >> But if we make dbg.value fully variadic with all parameters pushed to the stack ahead of time, we can distinguish between >> >> dbg.value(!DILocalVariable(x), DIExpression(DW_OP_constu, 42)) ; a constant >> dbg.value(i32 42, !DILocalVariable(x), DIExpression()) ; the same constant dbg.value(%undef, !DILocalVariable(x), DIExpression(DW_OP_constu, 42)) ; undef + garbage leftover expression >> >> If we do it this way and allow 0-n SSA values then I'm in support of the push-first scheme. > > This seems like as good a case as any to go with the push-first approach, since it solves a meaningful problem that the alternative does not. > >> As a side note, updating dbg.values in salvageDebugInfo when we allow multiple SSA values per dbg.value will be interesting, but possible. We'll need to put the value we need to update at the top of the stack, prepend the salvage expression and then restore the order of arguments on the stack that the rest of the expression expects. > > The difficulty here is that in DWARF5, there is no operator that can move an argument up the stack; there are specific operators that rotate the top 2 or 3 elements, but outside of that the only thing you can do is copy an element to the top with DW_OP_pick. Of course we don't have to stick to actual DWARF operators by any means, but I don't think there's any way to eventually express an indexed swap/move operator in DWARF either: you can duplicate an element from anywhere on the stack to the top, but you can't delete the original element or move the top element back down again. Because of this, it is necessary (I believe) to use DW_OP_pick in the general case even if all the arguments are initially pushed onto the stack, due to the limits on how we can manipulate the stack. Of course there are many expressions that don't require DW_OP_pick to evaluate, but for consistency's sake I think it's best to use it instead of the swap or rotate operators whenever any of them is needed. > > Regardless of whether we push everything immediately or not, having an operator that pushes the SSA value at a given argument index onto the stack seems necessary - if we push elements initially then DW_OP_pick (or DW_OP_LLVM_pick), otherwise DW_OP_LLVM_register. In either of these cases, salvaging is simple: we simply append the salvage expression after the salvaged value's operator. It also translates cleanly to DWARF, since we can directly replace the operator in question with DW_OP_regval_type, using whichever register holds the value we're looking for as an argument, without needing to modify or even consider the rest of the expression at all. >When we have <= 3 arguments (I don't want to call them registers because they may turn into constants or memory locations later on) we can use DW_OP_rot to move the one we need to adjust to the top and the DW_OP_rot the stack back into the original order. For 4-255 arguments, we need to add extra operations both to the beginning and the end of the expression, here an example for salvaging the 4th element from the top: r0 r1 r2 r3 ; stack before DW_OP_pick 0 r0 r1 r2 r3 r0 ; stack after pick 0 ... <adjustments> ; the salvage operations for r0 r0 r1 r2 r3 r0' DW_OP_pick 1 r0 r1 r2 r3 r0' r1 DW_OP_pick 2 r0 r1 r2 r3 r0' r1 r2 DW_OP_pick 3 r0 r1 r2 r3 r0' r1 r2 r3 <rest of expression> r0 r1 r2 r3 result DW_OP_swap r0 r1 r2 result r3 DW_OP_drop r0 r1 r2 result DW_OP_swap r0 r1 result r2 DW_OP_drop DW_OP_rot DW_OP_drop DW_OP_drop result It's possible to do with just DWARF expressions, but the resulting expression may get a little long. We could also just say we support up to 3 arguments and that would probably be fine. -- adrian -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20200225/6cbda1d5/attachment.html>