This is an archive of the discontinued LLVM Phabricator instance.

Add PointerType analysis for DirectX backend
ClosedPublic

Authored by beanz on Mar 22 2022, 4:13 PM.

Details

Summary

As implemented this patch assumes that Typed pointer support remains in
the llvm::PointerType class, however this could be modified to use a
different subclass of llvm::Type that could be disallowed from use in
other contexts.

This does not rely on inserting typed pointers into the Module, it just
uses the llvm::PointerType class to track and unique types.

Diff Detail

Event Timeline

beanz created this revision.Mar 22 2022, 4:13 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2022, 4:13 PM
beanz requested review of this revision.Mar 22 2022, 4:13 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 22 2022, 4:13 PM
python3kgae added inline comments.Mar 23 2022, 12:02 PM
llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
29

Why not use V->users() when all the access of Use are Use.getUser()?

37

Could a ptr used by AllocaInst?

41

In what case NewPointeeTy will be opaque?

51

In what case the pointee type cannot be determined?

beanz updated this revision to Diff 418045.Mar 24 2022, 2:16 PM

Moving to using dxil::TypedPointerType instead of llvm::PointerType.

Also addressed a few bugs and fleshed out support for function types.

kuhar added inline comments.Mar 25 2022, 10:39 AM
llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
62

nit: prefer early exits

97–100

Does this handle global aliases?

llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp
26

nit: Why 8? If the exact small size is not that important or know, I think it's better to use the default one: SmallVector<Type *> Types;

30–35

Is this the same as is_permutation(Types, Expected)?

57–58

I think it might be more helpful to first extract values from the map and then check, something like:

EXPECT_THAT(Ex, UnorderedElementsAreArray(make_second_range(Extracted)));

(I haven't tried to compile it, you may need to tweak this a bit.)

The main difference is that gmock macros explain what went wrong when check fail, e.g., which array elements were different.

62–75

Consider creating a test class that takes care of assembling input IR, creating a context, and running the analysis.

kuhar added inline comments.Mar 25 2022, 10:51 AM
llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp
57–58

correction, more like: EXPECT_THAT(make_second_range(Map), UnorderedElementsAre(I8Ptr, FnTy));

beanz updated this revision to Diff 418649.Mar 28 2022, 11:12 AM

Use llvm::Any instead of OpaqueDataStore.

nikic added inline comments.Apr 6 2022, 7:55 AM
llvm/include/llvm/IR/LLVMContext.h
328

The addition of this API should be separated into a separate review (with a unit test).

llvm/include/llvm/IR/Type.h
78

Hrm, it's unfortunate that this need to be exposed here.

llvm/lib/Target/DirectX/DXILPointerType.cpp
36

Why does this need shared_ptr?

nit: Drop braces for single-line if.

llvm/lib/Target/DirectX/DXILPointerType.h
3

File name out of sync, also in the header guard.

47

Do you actually need these helpers? The TODO certainly doesn't make sense...

llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
34

Are instructions like atomicrmw or cmpxchg relevant here?

44

Why does this one return directly rather than assigning NewPointeeTy?

58

Unnecessary llvm prefix.

90

Does not preserve variadic function type?

91

Spurious semicolon.

llvm/lib/Target/DirectX/PointerTypeAnalysis.h
17

Include doesn't look necessary.

31

Last two lines are outdated.

llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp
21

static and unnecessary llvm prefix.

25

But this doesn't check that the type is actually assigned to the correct value, right? Just that the type is present somewhere?

47

Comment doesn't make sense?

beanz added inline comments.Apr 6 2022, 8:43 AM
llvm/include/llvm/IR/Type.h
78

Sadly yes. That could be changed, but since we do this for X86 types too it is the current pattern.

llvm/lib/Target/DirectX/DXILPointerType.cpp
36

llvm::Any (and std::any) data types must be copyable, and it returns copies, not the original object. Since I don't want the underlying DenseMaps to be copied around a million times, needs to be a pointer, and unique_ptr isn't copyable.

llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
34

We don't support those instructions in DXIL. We're not yet handling them in DXILPrepare either, but they'll translate into intrinsics, and we'll need to expand type analysis to cover our intrinsics as they get added.

44

It is returning with a recursive call in the argument. This follows LLVM convention for early returns since the subsequent code isn't applicable.

90

We don't support variadic functions in the source language, so they shouldn't be showing up in the IR. I can add an assert to hard fail that case.

llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp
25

That's a fair observation. I can update the test to be more robust.

beanz updated this revision to Diff 422334.Apr 12 2022, 2:21 PM

Updating based on review feedback.

I should write a printer for the pointer type analysis to aid in debugging and testing, but unless others feel differently I'd like to hold that off to a future patch.

The rewritten test cases are a bit more verbose, but they are more strictly verifying the results.

This seems mostly reasonable. I'd point out that if you had an analysis printer, then test cases could be written much more conveniently as lit tests.

llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
48

I imagine this should be a TypedPointerType?

llvm/lib/Target/DirectX/PointerTypeAnalysis.h
2

Typo: Analysis

32

Should the namespace be DxilPointerTypeAnalysis since it's target-specific? Also, given that a namespace is used, should the using declaration above be part of the namespace? I don't feel particularly strongly on either point.

beanz added inline comments.Apr 13 2022, 6:10 PM
llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
48

It is meant to be the pointee type. In cases where the analysis detects more than one element type it falls back to an i8*, and relies on bitcasts being inserted.

beanz added inline comments.Apr 13 2022, 6:12 PM
llvm/lib/Target/DirectX/PointerTypeAnalysis.h
32

I put TypedPointerType in the dxil namespace, maybe that makes sense here too.

beanz updated this revision to Diff 422706.Apr 13 2022, 6:24 PM

Updates based on review feedback.

nhaehnle added inline comments.Apr 14 2022, 10:10 AM
llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
48

Gotcha, thanks.

llvm/lib/Target/DirectX/PointerTypeAnalysis.h
32

Yeah, that would make sense.

kuhar added inline comments.Apr 25 2022, 11:30 AM
llvm/lib/IR/AsmWriter.cpp
616

This prints dixl-ptr (0x...) instead of the element type. Is this intentional? Could we have a test for this?

llvm/lib/Target/DirectX/DXILPointerType.h
40

nit: do we need inline here?

llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
26

Make this a nested if instead. We don't need an else because PointeeTy is already initialized to nullptr above.

32

nit: const auto *User

43

What's the worst case for the number of recursive calls? Could a pathological shader cause a stack overflow?

97–100

ping

103

I find it a bit confusing that we have a helper function handleFunction yet still have to iterate over the function instructions here. Can you add a comment describing what handleFunction does? Or alternatively, could we move this for loop into handleFunction?

llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp
43

IMO it would be more readable/idiomatic to use gmock matchers here, e.g.:

#include "gmock/gmock.h"

using ::testing::Contains;
using ::testing::Pair;

template <typename T> struct IsA {
  friend bool operator==(const Value *V, const IsA &) { return isa<T>(V); }
};

// ...
  ASSERT_EQ(Map.size(), 2u);
  Type *I8Ptr = TypedPointerType::get(Type::getInt8Ty(Context), 0);
  Type *FnTy = FunctionType::get(Type::getInt64Ty(Context), {I8Ptr}, false);
  EXPECT_THAT(Map, Contains(Pair(IsA<Function>(), FnTy)));
  EXPECT_THAT(Map, Contains(Pair(IsA<ReturnInst>(), I8Ptr)))

Or if you would like something with descriptive failure messages:

template <typename T> bool HasType(const Value *V) { return isa<T>(V); }

MATCHER_P2(HasOneEntry, key_predicate, value_type, "") {
  bool Found = false;
  for (auto &KV : arg) {
    if (key_predicate(KV.first)) {
      if (Found) {
        *result_listener << "where duplicate keys were found";
        return false;
      }
      if (KV.second != value_type) {
        *result_listener << "where the key was found but value did not match";
        return false;
      }
      Found = true;
    }
  }

  if (!Found) {
    *result_listener << "where the key is missing";
    return false;
  }
  return true;
}

// ...

  EXPECT_THAT(Map, HasOneEntry(HasType<ReturnInst>, I8Ptr));
209

Could we have a new test case that deals with globals?

beanz added a comment.Apr 25 2022, 2:34 PM

Comments below. Updated patch coming shortly.

llvm/lib/IR/AsmWriter.cpp
616

Yea, I did this intentionally so that the llvm::Type class doesn't need to preserve interfaces for typed pointers, and so that the impact of DXILPointerType outside the backend is kept at a minimum.

llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
43

Since the source language doesn't support pointers, we should rarely see more than two levels of indirection.

97–100

Not yet. I'm not yet handling any globals.

103

The poorly named handleFunction helper just generates the function type. The loop for instructions inside only runs if the return type is an opaque pointer to collect the possible return types. I'll add some comments explaining and rename the function more appropriately.

beanz updated this revision to Diff 425035.Apr 25 2022, 2:41 PM

Updates based on review feedback. Thanks @kuhar!

kuhar added inline comments.Apr 25 2022, 2:41 PM
llvm/lib/IR/AsmWriter.cpp
616

This is surprising to me, I'd expect types to produce the same strings across multiple runs. Could ignore the addresses altogether, use some stable ID, or add a comment explaining this choice here?

kuhar added inline comments.Apr 25 2022, 2:42 PM
llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
43

OK, that makes sense to me. Could you add this as a comment?

beanz added a comment.Apr 25 2022, 2:42 PM

Ugh... please hold off reviewing. I somehow messed up the commit update and got a lot of noisy diffs.

kuhar added inline comments.Apr 25 2022, 2:47 PM
llvm/unittests/Target/DirectX/PointerTypeAnalysisTests.cpp
185–186

nit: I think the convention is that we either have all if/else bodies in a chain with or without braces. So

if (...)
  ...;
else
  ...;

and

if (...) {
  ...
} else {
  ...;
}

are fine, but:

if (...) {
  ...;
} else
  ...;

is not.

beanz updated this revision to Diff 425039.Apr 25 2022, 2:55 PM

This should fix the patch upload, also added in some more of @kuhar's suggestions :).

kuhar accepted this revision.Apr 25 2022, 3:00 PM

LGTM modulo nits. Thanks for the fixes!

llvm/lib/IR/AsmWriter.cpp
616

also this

llvm/lib/Target/DirectX/PointerTypeAnalysis.cpp
22

nit: s/classify/Classifies

43

also this

This revision is now accepted and ready to land.Apr 25 2022, 3:00 PM
beanz added inline comments.Apr 25 2022, 3:15 PM
llvm/lib/IR/AsmWriter.cpp
616

Yea. This is an odd case. I'll add a comment. This isn't the only case where we print the type pointer's address in the dump method. It also happens for StructTypes in some cases.

This revision was landed with ongoing or failed builds.Apr 25 2022, 3:55 PM
This revision was automatically updated to reflect the committed changes.