Skip to content

[mlir] Add support for recursive elements in DICompositeTypeAttr. #74948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 55 additions & 21 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td
Original file line number Diff line number Diff line change
Expand Up @@ -351,27 +351,6 @@ def LLVM_DICompileUnitAttr : LLVM_Attr<"DICompileUnit", "di_compile_unit",
let assemblyFormat = "`<` struct(params) `>`";
}

//===----------------------------------------------------------------------===//
// DICompositeTypeAttr
//===----------------------------------------------------------------------===//

def LLVM_DICompositeTypeAttr : LLVM_Attr<"DICompositeType", "di_composite_type",
/*traits=*/[], "DITypeAttr"> {
let parameters = (ins
LLVM_DITagParameter:$tag,
OptionalParameter<"StringAttr">:$name,
OptionalParameter<"DIFileAttr">:$file,
OptionalParameter<"uint32_t">:$line,
OptionalParameter<"DIScopeAttr">:$scope,
OptionalParameter<"DITypeAttr">:$baseType,
OptionalParameter<"DIFlags", "DIFlags::Zero">:$flags,
OptionalParameter<"uint64_t">:$sizeInBits,
OptionalParameter<"uint64_t">:$alignInBits,
OptionalArrayRefParameter<"DINodeAttr">:$elements
);
let assemblyFormat = "`<` struct(params) `>`";
}

//===----------------------------------------------------------------------===//
// DIDerivedTypeAttr
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -684,6 +663,61 @@ def LLVM_AliasScopeDomainAttr : LLVM_Attr<"AliasScopeDomain",
let assemblyFormat = "`<` struct(params) `>`";
}

//===----------------------------------------------------------------------===//
// DICompositeTypeAttr
//===----------------------------------------------------------------------===//

def LLVM_DICompositeTypeAttr : LLVM_Attr<"DICompositeType", "di_composite_type",
/*traits=*/[NativeTypeTrait<"IsMutable">], "DITypeAttr"> {
let parameters = (ins
OptionalParameter<"unsigned">:$tag,
OptionalParameter<"StringAttr">:$name,
OptionalParameter<"DIFileAttr">:$file,
OptionalParameter<"uint32_t">:$line,
OptionalParameter<"DIScopeAttr">:$scope,
OptionalParameter<"DITypeAttr">:$baseType,
OptionalParameter<"DIFlags", "DIFlags::Zero">:$flags,
OptionalParameter<"uint64_t">:$sizeInBits,
OptionalParameter<"uint64_t">:$alignInBits,
OptionalArrayRefParameter<"DINodeAttr">:$elements,
OptionalParameter<"StringAttr">:$identifier
);
let hasCustomAssemblyFormat = 1;
let genStorageClass = 0;
let storageClass = "DICompositeTypeAttrStorage";
let builders = [
AttrBuilder<(ins
"unsigned":$tag,
"StringAttr":$name,
"DIFileAttr":$file,
"uint32_t":$line,
"DIScopeAttr":$scope,
"DITypeAttr":$baseType,
"DIFlags":$flags,
"uint64_t":$sizeInBits,
"uint64_t":$alignInBits,
"::llvm::ArrayRef<DINodeAttr>":$elements
)>,
AttrBuilder<(ins
"StringAttr":$identifier,
"unsigned":$tag,
"StringAttr":$name,
"DIFileAttr":$file,
"uint32_t":$line,
"DIScopeAttr":$scope,
"DITypeAttr":$baseType,
"DIFlags":$flags,
"uint64_t":$sizeInBits,
"uint64_t":$alignInBits,
CArg<"::llvm::ArrayRef<DINodeAttr>", "{}">:$elements
)>
];
let extraClassDeclaration = [{
static DICompositeTypeAttr getIdentified(MLIRContext *context, StringAttr identifier);
void replaceElements(const ArrayRef<DINodeAttr>& elements);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void replaceElements(const ArrayRef<DINodeAttr>& elements);
void replaceElements(ArrayRef<DINodeAttr> elements);

nit: just ArrayRef should be sufficient since it is a constant reference on its own.

}];
}

//===----------------------------------------------------------------------===//
// AliasScopeAttr
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 4 additions & 0 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMAttrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
namespace mlir {
namespace LLVM {

namespace detail {
struct DICompositeTypeAttrStorage;
} // namespace detail

/// This class represents the base attribute for all debug info attributes.
class DINodeAttr : public Attribute {
public:
Expand Down
2 changes: 1 addition & 1 deletion mlir/include/mlir/Target/LLVMIR/Dialect/All.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ registerAllGPUToLLVMIRTranslations(DialectRegistry &registry) {
registerLLVMDialectTranslation(registry);
registerNVVMDialectTranslation(registry);
registerROCDLDialectTranslation(registry);
registerSPIRVDialectTranslation(registry);
//registerSPIRVDialectTranslation(registry);

// Extension required for translating GPU offloading Ops.
gpu::registerOffloadingLLVMTranslationInterfaceExternalModels(registry);
Expand Down
1 change: 1 addition & 0 deletions mlir/lib/AsmParser/AttributeParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ Attribute Parser::parseAttribute(Type type) {
OptionalParseResult Parser::parseOptionalAttribute(Attribute &attribute,
Type type) {
switch (getToken().getKind()) {
case Token::kw_distinct:
case Token::at_identifier:
case Token::floatliteral:
case Token::integer:
Expand Down
136 changes: 136 additions & 0 deletions mlir/lib/Dialect/LLVMIR/IR/AttrDetail.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//===- AttrDetail.h - Details of MLIR LLVM dialect attributes --------*- C++
//-*-===//
Comment on lines +1 to +2
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:nit overflow :)

//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains implementation details, such as storage structures, of
// MLIR LLVM dialect attributes.
//
//===----------------------------------------------------------------------===//
#ifndef DIALECT_LLVMIR_IR_ATTRDETAIL_H
Comment on lines +13 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
//===----------------------------------------------------------------------===//
#ifndef DIALECT_LLVMIR_IR_ATTRDETAIL_H
//===----------------------------------------------------------------------===//
#ifndef DIALECT_LLVMIR_IR_ATTRDETAIL_H

nit: newline

#define DIALECT_LLVMIR_IR_ATTRDETAIL_H

#include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
#include "mlir/IR/Types.h"

namespace mlir {
namespace LLVM {
namespace detail {

//===----------------------------------------------------------------------===//
// DICompositeTypeAttrStorage
//===----------------------------------------------------------------------===//

struct DICompositeTypeAttrStorage : public ::mlir::AttributeStorage {
using KeyTy = std::tuple<unsigned, StringAttr, DIFileAttr, uint32_t,
DIScopeAttr, DITypeAttr, DIFlags, uint64_t, uint64_t,
ArrayRef<DINodeAttr>, StringAttr>;

DICompositeTypeAttrStorage(unsigned tag, StringAttr name, DIFileAttr file,
uint32_t line, DIScopeAttr scope,
DITypeAttr baseType, DIFlags flags,
uint64_t sizeInBits, uint64_t alignInBits,
ArrayRef<DINodeAttr> elements,
StringAttr identifier = StringAttr())
: tag(tag), name(name), file(file), line(line), scope(scope),
baseType(baseType), flags(flags), sizeInBits(sizeInBits),
alignInBits(alignInBits), elements(elements), identifier(identifier) {}

unsigned getTag() const { return tag; }
StringAttr getName() const { return name; }
DIFileAttr getFile() const { return file; }
uint32_t getLine() const { return line; }
DIScopeAttr getScope() const { return scope; }
DITypeAttr getBaseType() const { return baseType; }
DIFlags getFlags() const { return flags; }
uint64_t getSizeInBits() const { return sizeInBits; }
uint64_t getAlignInBits() const { return alignInBits; }
ArrayRef<DINodeAttr> getElements() const { return elements; }
StringAttr getIdentifier() const { return identifier; }

/// Returns true if this attribute is identified.
bool isIdentified() const {
return !(!identifier);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return !(!identifier);
return identifier;

Wouldn't this work?

Copy link
Contributor Author

@waj334 waj334 Dec 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope it should be !!identifier. It's weird, but by itself it is not evaluated as a bool

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh 🦌, maybe static_cast<bool>(identifier) works or otherwise !!identifier is fine as well.

}

/// Returns the respective key for this attribute.
KeyTy getAsKey() const {
if (isIdentified())
return KeyTy(tag, name, file, line, scope, baseType, flags, sizeInBits,
alignInBits, elements, identifier);

return KeyTy(tag, name, file, line, scope, baseType, flags, sizeInBits,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the distinction here necessary? I would assume the identifier (in the future hopefully id) is probably anyways default initialized to nullptr?

alignInBits, elements, StringAttr());
}

/// Compares two keys.
bool operator==(const KeyTy &other) const {
if (isIdentified())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like you design choice of only using the distinct attribute for uniquing (when computing the has and the operation==) but returning everything with getKeyAs which is nice when walking the subelements of the attribute. Can you add some comments that explain these design choices for the next reviewer / next person modifying the code.

// Just compare against the identifier.
return identifier == std::get<10>(other);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably put the ID at index 0.


// Otherwise, compare the entire tuple.
return other == getAsKey();
}

/// Returns the hash value of the key.
static llvm::hash_code hashKey(const KeyTy &key) {
const auto &[tag, name, file, line, scope, baseType, flags, sizeInBits,
alignInBits, elements, identifier] = key;

if (identifier)
// Only the identifier participates in the hash id.
return hash_value(identifier);

// Otherwise, everything else is included in the hash.
return hash_combine(tag, name, file, line, scope, baseType, flags,
sizeInBits, alignInBits, elements);
}

/// Constructs new storage for an attribute.
static DICompositeTypeAttrStorage *
construct(AttributeStorageAllocator &allocator, const KeyTy &key) {
auto [tag, name, file, line, scope, baseType, flags, sizeInBits,
alignInBits, elements, identifier] = key;
elements = allocator.copyInto(elements);
if (identifier) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the separate handling here really necessary? I would assume if identifier is nullptr we can just pass it on to the constructor?

return new (allocator.allocate<DICompositeTypeAttrStorage>())
DICompositeTypeAttrStorage(tag, name, file, line, scope, baseType,
flags, sizeInBits, alignInBits, elements,
identifier);
}
return new (allocator.allocate<DICompositeTypeAttrStorage>())
DICompositeTypeAttrStorage(tag, name, file, line, scope, baseType,
flags, sizeInBits, alignInBits, elements);
}

LogicalResult mutate(AttributeStorageAllocator &allocator,
const ArrayRef<DINodeAttr>& elements) {
// Replace the elements.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I remember, most mutable upstream attributes implement a check here to ensure the elements can only be set once (or if set repeatedly they need to be set to the same value). I think such a check would be helpful here since that is presumably an error in our case (e.g. if the import sets the elements to two different values this probably indicates a collision of different LLVM DICompositeTypes to a single attribute in MLIR).

this->elements = allocator.copyInto(elements);
return success();
}

private:
unsigned tag;
StringAttr name;
DIFileAttr file;
uint32_t line;
DIScopeAttr scope;
DITypeAttr baseType;
DIFlags flags;
uint64_t sizeInBits;
uint64_t alignInBits;
ArrayRef<DINodeAttr> elements;
StringAttr identifier;
};

} // namespace detail
} // namespace LLVM
} // namespace mlir

#endif // DIALECT_LLVMIR_IR_ATTRDETAIL_H
Loading