This is an archive of the discontinued LLVM Phabricator instance.

[PassManager/Sanitizer] Port of AddresSanitizer pass from legacy to new PassManager
ClosedPublic

Authored by leonardchan on Oct 1 2018, 11:41 AM.

Details

Summary

This patch ports the legacy pass manager to the new one to take advantage of the benefits of the new PM. This involved moving a lot of the declarations for AddressSantizer to a header so that it can be publicly used via PassRegistry.def which I believe contains all the passes managed by the new PM.

This patch essentially decouples the instrumentation from the legacy PM such that it can be used by both legacy and new PM infrastructure.

Diff Detail

Repository
rL LLVM

Event Timeline

leonardchan created this revision.Oct 1 2018, 11:41 AM
leonardchan added a subscriber: silvas.
  • Added LLVM header description

Thank you for moving this along!

You can greatly reduce this change by pulling most of the functionality out of the pass, and just wrapping it up in per-PassManager wrapper passes. You also don't have to move all that code into the header and can keep it local in the implementation.

Thank you for moving this along!

You can greatly reduce this change by pulling most of the functionality out of the pass, and just wrapping it up in per-PassManager wrapper passes. You also don't have to move all that code into the header and can keep it local in the implementation.

Well, it will be a bit tricky to avoid moving the interfaces around, since AddressSanitizer contains quite a load of state, so you cant have a wrapper w/o introducing
definitions of this state other than playing tricks with indirection.
Though yes, making wrapper passes that have a pointer to opaque AddressSanitizer might do the dirty trick.

  • Reduce size of patch by adding per-PM wrappers for AddressSanitizer and AddressSanitizerModule. The passes exposed to the new PM use opaque pointers to these wrappers.
fedor.sergeev added inline comments.Oct 2 2018, 6:01 AM
include/llvm/IR/AddressSanitizerPass.h
23 ↗(On Diff #167866)

I dont believe you need any extra wrappers in addition to AddressSanitizer*Pass ones.

Just use class AddressSanitizer/AddresSanitizerModule here.

leonardchan marked an inline comment as done.
leonardchan added inline comments.
include/llvm/IR/AddressSanitizerPass.h
23 ↗(On Diff #167866)

Done, but this would require moving AddressSanitizer, AddressSanitizerModule, and FunctionStackPoisoner outside of their anonymous namespace.

Well, it will be a bit tricky to avoid moving the interfaces around, since AddressSanitizer contains quite a load of state

What kind of state is asan tracking that persists across functions or modules?

Well, it will be a bit tricky to avoid moving the interfaces around, since AddressSanitizer contains quite a load of state

What kind of state is asan tracking that persists across functions or modules?

For AddressSanitizer I think this might refer to the GlobalsMetaData and ShadowMapping made during initialization, but I think they get reset on each function run. All tests with the legacy PM still seem to pass though which I think indicates that the logic is still sound even with the ASan functionality separated from the legacy pass.

Actually I was asking this because as far as I can tell, asan doesn't track state across functions and modules. Which means there's no point in _retaining_ the state, i.e. you don't have to keep an instance of it around.

*Ping*

Are there any other changes that would be necessary before getting revision acceptance?

See my latest comment. If there is no state to be kept, that state need not be on the exported interface.

See my latest comment. If there is no state to be kept, that state need not be on the exported interface.

My bad. I misinterpreted it. Removed the sanitizer as a member so it is created locally on new runs and readded AddressSanitizer and AddressSanitizerModule back to their anonymous namespaces.

Some comments inline. Does it make sense to merge the module and function passes into one? Is it really necessary to distinguish the passes?

include/llvm/IR/AddressSanitizerPass.h
23 ↗(On Diff #168692)

The PassInfoMixin takes care of the name function for you.

37 ↗(On Diff #168692)

Use the PassInfoMixin.

lib/Transforms/Instrumentation/AddressSanitizer.cpp
610 ↗(On Diff #168692)

This isn't a pass anymore, it doesn't need this function.

739 ↗(On Diff #168692)

Unneeded function.

1047 ↗(On Diff #168692)

The convention is to name it AddressSanitizerLegacyPass

1081 ↗(On Diff #168692)

The convention is to name it AddressSanitizerModuleLegacyPass

1124 ↗(On Diff #168692)

Just make this an if.

1131 ↗(On Diff #168692)

As above, just make this an if

2538 ↗(On Diff #168692)

Instead of doing this kind of lazy initialization, just do it in the constructor.

leonardchan marked 9 inline comments as done.

Some comments inline. Does it make sense to merge the module and function passes into one? Is it really necessary to distinguish the passes?

This I'm not sure of since it seems that they both do different things that work independently of each other, though I can't find an example/test where they are used on their own. @vitalybuka may be able to offer more insight into this. Otherwise if it's necessary to combine the passes, I can create a separate patch that does this and changes any relevant tests.

include/llvm/IR/AddressSanitizerPass.h
23 ↗(On Diff #167866)

Moved them back into their anonymous namespace.

lib/Transforms/Instrumentation/AddressSanitizer.cpp
2538 ↗(On Diff #168692)

This initialization requires access to the Module that this pass runs over but I don't think I have access to that in the constructor.

This I'm not sure of since it seems that they both do different things that work independently of each other, though I can't find an example/test where they are used on their own.

I meant just merging the passes. In the sense that you have one pass class with one module run() method and one function run() method. The question then is whether you'll ever want to use different options for the different IRUnits.

lib/Transforms/Instrumentation/AddressSanitizer.cpp
2538 ↗(On Diff #168692)

Yes you do. You're now constructing one instance of this per pass run, you can just pass along the module there.

leonardchan marked 3 inline comments as done.

I meant just merging the passes. In the sense that you have one pass class with one module run() method and one function run() method. The question then is whether you'll ever want to use different options for the different IRUnits.

I see. As far as I can tell, both passes actually use the same options for CompileKernel and Recover when creating new passes, but UseAfterScope is different for both in that the option is passed from a code gen option for AddressSanitizer whereas AddressSanitizerModule sets UseAfterScope as true by default and is dependent on if ASan uses garbage collection friendly instrumentation for globals. I think this is a reason to keep them separate.

lib/Transforms/Instrumentation/AddressSanitizer.cpp
2538 ↗(On Diff #168692)

My bad. Didn't notice that.

I see. As far as I can tell, both passes actually use the same options for CompileKernel and Recover when creating new passes, but UseAfterScope is different for both in that the option is passed from a code gen option for AddressSanitizer whereas AddressSanitizerModule sets UseAfterScope as true by default and is dependent on if ASan uses garbage collection friendly instrumentation for globals.

These are different per instance though. Even if merged, you're free to create one instance with options A and one instance with options B and run the first instance on a function pipeline and the second on a module pipeline.

include/llvm/IR/AddressSanitizerPass.h
17 ↗(On Diff #168743)

Why is this include required?

Also, you're missing PassManager.h for AnalysisManager and PassInfoMixin

23 ↗(On Diff #168743)

These classes need comments.

I also noticed you got the header in the wrong place. It should live in Transforms/Instrumentation, not in IR.

leonardchan marked 2 inline comments as done.

These are different per instance though. Even if merged, you're free to create one instance with options A and one instance with options B and run the first instance on a function pipeline and the second on a module pipeline.

In that case then, it seems like there's nothing wrong with combining them since for any pipeline you could create different instances of this combined pass with different options.

We're getting there! Some more tiny nits.

include/llvm/Transforms/Instrumentation/AddressSanitizerPass.h
32 ↗(On Diff #168839)

These are called FunctionAnalysisManager and ModuleAnalysisManager. Coincidentally these are just typedefs for the same things, but that's not guaranteed, e.g. AnalysisManager<Loop> != LoopAnalysisManager.

38 ↗(On Diff #168839)

No, this shouldn't be needed. You will be using one instance per PM type.

lib/Passes/PassRegistry.def
43 ↗(On Diff #168839)

The module suffix is superfluous.

lib/Transforms/Instrumentation/AddressSanitizer.cpp
2538 ↗(On Diff #168839)

I'd find it nicer if the DomintarTree were passed during construction instead.

leonardchan marked 5 inline comments as done.
philip.pfaffe accepted this revision.Oct 11 2018, 7:34 AM

This looks great now, thank you!

This revision is now accepted and ready to land.Oct 11 2018, 7:34 AM
This revision was automatically updated to reflect the committed changes.