Page MenuHomePhabricator

[Debugify] Introduce debugify-each and DebugifyFunctionPass

Authored by tyb0807 on May 7 2018, 6:58 AM.




As discussed in this thread on the mailing list, here's my attempt to implement a debugify-each mode and to create a DebugifyFunctionPass.

If you are ok with this approach, I will implement a DebugifyBasicBlockPass, a DebugifyLoopPass and a DebugifyRegionPass likewise.

Another TODO is to make debugify-each as well as Debugify passes other than DebugifyModulePass work with the new Pass Manager, but this is not necessary for the GSoC (-O1/2/3/s/z optimization pipelines are handled by the LegacyPassManager). But I am willing to tackle it if you do not have anything against.

This is my first major patch, so please forgive me if I've done anything wrong...

Thank you all for your help

Diff Detail


Event Timeline

tyb0807 created this revision.May 7 2018, 6:58 AM
vsk added a comment.May 7 2018, 11:35 AM

Thanks for the patch. I have some comments inline.

Let's land debugify-each support for module and function passes before tackling other types of passes.

31 ↗(On Diff #145457)

Could you move the tests for -debugify-each to a new file? This one is getting hard to follow.

31 ↗(On Diff #145457)

In the new test for -debugify-each, we should test opt -O1 -debugify-each and check that at least one module pass and function pass are instrumented & checked.

65 ↗(On Diff #145457)

We should assert that the named metadata here has exactly 2 operands, as it's not immediately clear from the way the debugify function pass is invoked that this is the case.

191 ↗(On Diff #145457)

These bitvectors should be passed as references to avoid copies ('const BitVector &').

207 ↗(On Diff #145457)

Why is the explicit cast needed?

279 ↗(On Diff #145457)

The extra parens are not needed here -- you can just write 'ShouldApply = !...'

312 ↗(On Diff #145457)

You can move this to the initialization list as 'Strip(Strip)'.

357 ↗(On Diff #145457)


264 ↗(On Diff #145457)

How about a more general name, like 'OptCustomPassManager'?

268 ↗(On Diff #145457)

The DebugifyEach flag doesn't need to be passed in here.

282 ↗(On Diff #145457)

Hm, this misses the 'Print Function IR' pass. There should be a less brittle way to do this. Could you add a 'bool isIRPrintingPass(Pass *P)' function to IRPrintingPasses.h? You can inspect the address of the pass ID to implement the predicate.

287 ↗(On Diff #145457)

Please use fall through to fold bodies for the identical cases together.

tyb0807 updated this revision to Diff 145898.May 9 2018, 5:22 AM
tyb0807 marked 12 inline comments as done.

Hello Vedant,

Thanks for your comments. I've updated the patch accordingly.

vsk added a comment.May 9 2018, 11:28 AM

Thanks! At a high level this is looking good. I have more comments inline which should help with code reuse. Currently there is some extra duplicated logic for function passes, and this should be consolidated for easier maintenance.

209 ↗(On Diff #145898)

Why is this RAUW needed at all? Should StripDebugInfo handle this for you?

242 ↗(On Diff #145898)

You shouldn't need to duplicate any logic for setting up these BitVectors, and you shouldn't need a separate printCheckDebugifyResults() function.

Please reuse the same logic for checking debugify metadata in module and function passes. E.g:

void checkDebugifyMetadata(Module &M, iterator_range<Module::iterator> Functions) {
  // Get the number of lines / vars from the module's named metadata, set up the bit vectors, etc.
  for (Function &F : Functions) {
    // Check for missing lines / vars.
  // Print the results.

// In the FunctionPass helper:
checkDebugifyMetadata(M, make_range(F.getIterator(), std::next(F.getIterator()))); //< Just check "F".

// In the ModulePass helper:
checkDebugifyMetadata(M, M.functions());

If in the future we add a debugify wrapper for BasicBlock passes, we can simply add a 'BasicBlock *' parameter to 'checkDebugifyMetadata'.

262 ↗(On Diff #145898)

Per my previous comments, I'd suggest making two changes here:

  • Move all of the logic to a helper to make a future port to the new PM easier.
  • Make applyDebugifyMetadata accept a range of functions to visit (iterator_range<Module::iterator>), so there is only one call to createCompileUnit, etc. in this file.
281 ↗(On Diff #145898)

I don't think we should compute and store ShouldApply in the doInitialization() step, because it happens just once per pass. It should be done in the applyDebugifyMetadata function.

324 ↗(On Diff #145898)

Please move all of the actual logic here into a helper function, to simplify a future port to the new pass manager.

67 ↗(On Diff #145898)

I think this is dead code.

264 ↗(On Diff #145898)

You can use an idiom here to reduce repetition and simplify the code:

using super = legacy::PassManager;
269 ↗(On Diff #145898)

Please use an early-return for the cases (outside of the switch) where we don't insert extra passes. E.g:

bool WrapWithDebugify = DebugifyEach && !P->getAsImmutablePass() && !isIRPrintingPass(P);
if (!WrapWithDebugify) {
284 ↗(On Diff #145898)

We can safely skip RegionPass and CGSCCPass, as they are just used for structurize-cfg, the old inliner, and 2-3 other passes. I don't think it's worth adding the complexity to support this, so we can leave it out of the TODO.

626 ↗(On Diff #145898)

This conditional shouldn't be written out twice. Could you introduce 'bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach'?

vsk added a comment.May 9 2018, 11:30 AM

One last point: to aid debugging, it would help if you could dump the Module / Function IR when the check-debugify step fails. Currently, if a pass fails checking, opt simply moves on without printing any helpful resources.

tyb0807 updated this revision to Diff 146298.EditedMay 11 2018, 3:52 AM
tyb0807 marked 10 inline comments as done.

Address review comments. Now we print out the module when check-debugify reports an error. I guess another important information should be the optimization pass that makes check-debugify fail, but I'm not sure how to get that information though.

P/S: Thanks Vedant, the code looks a lot more elegant now!

vsk added a comment.May 11 2018, 11:12 AM

Thanks for working on this @tyb0807, it looks really close :). There are just a few more minor things I'd like addressed.

1 ↗(On Diff #146298)

At a high level, we don't want to test the debugify logic in this file, because we already have a test for that. Instead, we want to check that -debugify-each is running after each pass.

Let's minimize this test:

  • Please remove the checks for what the inserted debug info metadata looks like.
  • We just need two functions, e.g "foo" and "bar", which both only contain "ret void".
  • Add RUN lines for: "-debugify-each -O{1,2,3}", and "-enable-debugify -debugify-each -sroa -sccp". All of these separate test runs should share the exact same FileCheck "CHECK" commands. Specifically, we expect the module & function debugify passes to run at least twice. So you could write:
; Verify that the module & function (check-)debugify passes run at least twice.

; CHECK: CheckModuleDebugify: PASS
; CHECK: CheckFunctionDebugify: PASS
; CHECK: CheckFunctionDebugify: PASS

; CHECK: CheckModuleDebugify: PASS
; CHECK: CheckFunctionDebugify: PASS
; CHECK: CheckFunctionDebugify: PASS

These should be the only CHECK lines you need.

44 ↗(On Diff #146298)

Could you use a StringRef here to avoid copying the std::string?

56 ↗(On Diff #146298)

Could you run clang-format? There are some whitespace-only changes which complicate the diff.

129 ↗(On Diff #146298)

Ditto, please use StringRef.

160 ↗(On Diff #146298)

We should only need special handling for "line 0", as that refers to a compiler-generated "ambiguous" location. I.e, if (DL && DL.getLine() != 0) { reset(...) }.

We don't need need any other range checks, because an assertion in BitVector should catch out-of-bounds accesses. This means we can omit the "if (DL)" which follows.

183 ↗(On Diff #146298)

As this change doesn't change any behavior, let's keep the previous version to minimize the diff.

207 ↗(On Diff #146298)

I still don't understand why we need to erase the declaration of llvm.dbg.value() from the module. Shouldn't StripDebugInfo() do this for us? If not, we should just fix StripDebugInfo() in a follow-up patch.

230 ↗(On Diff #146298)

Please replace "to everything" with "to instructions within a single function".

233 ↗(On Diff #146298)

Just a nit, but this could be easier to read if it's split up into logically smaller pieces, e.g:

Module &M = *F.getParent();
auto FuncIt = F.getIterator();
return apply...(M, make_range(FuncIt, std::next(FuncIt)), ...);
271 ↗(On Diff #146298)

Same nitpick about simplifying this callsite.

267 ↗(On Diff #146298)

The implicit default constructor here calls the base constructor. You can just leave this line out -- the compiler does it for you.

vsk added inline comments.May 11 2018, 11:16 AM
1 ↗(On Diff #146298)

Actually, s/-sroa/-instrprof -sroa/, because you'll want to run some ModulePass. It would also be fine to use CHECK-DAG here, to avoid a dependence on the pass order.

vsk added inline comments.May 11 2018, 11:22 AM
298 ↗(On Diff #146298)

You don't need to define this function either.

tyb0807 updated this revision to Diff 146430.May 11 2018, 3:49 PM
tyb0807 marked 13 inline comments as done.

Addressed review comments.

I am thinking about making this work for BasicBlockPass and LoopPass. How can I make an empty range in LLVM (for these passes, function iterator should contain nothing)?

Does something like make_range(std::next(BB.getParent()->getIterator()), std::next(BB.getParent()->getIterator())) make sense?

Another question is, what if a Loop contains BasicBlocks from different Functions? I guess we can just pass an empty range for the function iterator parameter, and add another loop over a basic block iterator parameter in applyDebugifyMetadata and checkDebugifyMetadata.

Thanks again for your help Vedant.

vsk added a comment.May 11 2018, 4:15 PM

Addressed review comments.

I am thinking about making this work for BasicBlockPass and LoopPass.

Would you mind discussing this on IRC, or llvm-dev? Stepping back a bit, I think it'd make sense to prioritize finding & fixing bugs with -debugify-each before making it more granular. Once there's more experience using the testing tool, we may develop better ideas for improvements.

51 ↗(On Diff #146430)

Why does this function need to be modified?

tyb0807 updated this revision to Diff 146464.May 12 2018, 12:55 AM
tyb0807 marked an inline comment as done.

Sorry it was a local change, forgot to remove it before diffing. Please tell me if there's any way to handle local change while still being able to create patch properly.

So do you need anything else (even if it is not in debugify)? I am willing to help!

vsk accepted this revision as: vsk.May 12 2018, 10:46 AM

LGTM. If you don’t have commit access, let me know, and I can commit this for you on Monday.

Sorry it was a local change, forgot to remove it before diffing. Please tell me if there's any way to handle local change while still being able to create patch properly.

No problem. If you use git, you can use “git -p” to selectively stage changes, commit them locally, and then “git stash” any remaining changes which shouldn’t be in the diff.

So do you need anything else (even if it is not in debugify)? I am willing to help!

Thanks again for your help. There’s certainly more to do. Some ideas:

  • Extend the -verify-each mode in opt, so that it also is enabled after each module/function pass.
  • Run opt -O1 with -debugify-each on input .ll files generated from clang, and see if you can find/fix debug info loss bugs.

Others in the community will have more ideas, so it’s worth asking around on IRC or LLVM-dev.

This revision is now accepted and ready to land.May 12 2018, 10:46 AM

Thank you Vedant. I do not have commit access, please commit this for me.

207 ↗(On Diff #146298)

I guess it should do this, but actually it doesn't. Removing this here and submitting another patch to fix StripDebugInfo()

281 ↗(On Diff #145898)

Oh well the reason for ShouldApply is that I don't want to print the error message for every function. I agree that this looks ugly but if you're ok with having the message printed multiple times for the same check pass then I'll remove this

This revision was automatically updated to reflect the committed changes.
mattd added a subscriber: mattd.May 14 2018, 5:35 PM