Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cff1cd3
[MLIR][LLVM] Implement LLVM dialect support for global aliases
bcardosolopes Jan 24, 2025
8ccafc9
Fix comments
bcardosolopes Jan 31, 2025
e36ff90
Remove undef from testcase
bcardosolopes Jan 31, 2025
f12825e
Address Tobias review
bcardosolopes Feb 3, 2025
1186559
Add addrspace related tests
bcardosolopes Feb 3, 2025
1927cb5
Reorganize tests a bit, use -split-input-file
bcardosolopes Feb 3, 2025
7b27f3a
More verification love
bcardosolopes Feb 3, 2025
82ca97a
Refactor common parsing bits for GlobalOp and AliasOp
bcardosolopes Feb 3, 2025
8711298
Add roundtrip tests for the parsing/printing
bcardosolopes Feb 3, 2025
3eb7a53
Fix more nits and align linkage verification with LLVM
bcardosolopes Feb 4, 2025
f159dcc
Fix one more comment
bcardosolopes Feb 4, 2025
cf984ad
Update parse comment
bcardosolopes Feb 4, 2025
77661e7
Handle conversion for globals/aliases together since they can referen…
bcardosolopes Feb 4, 2025
b669463
Teach LLVM_AddressOfOp to work with alias symbols, fix remaining bugs…
bcardosolopes Feb 4, 2025
644424d
Improve LLVM_AddressOfOp description doc
bcardosolopes Feb 4, 2025
0d181ee
More tablegen fixes, new test file in Dialect
bcardosolopes Feb 4, 2025
3932b16
Address more review comments
bcardosolopes Feb 4, 2025
d8bd0ab
More split-input-file love
bcardosolopes Feb 4, 2025
8683355
Fix multiple printing of addressofs in init bodies
bcardosolopes Feb 4, 2025
d69436c
Get rid of address space as top level AliasOp attribute
bcardosolopes Feb 5, 2025
f0755a4
Fix test, removed too much from globals
bcardosolopes Feb 5, 2025
e4346f6
Add one more verifier check to addressof
bcardosolopes Feb 5, 2025
334d643
address more reviews
bcardosolopes Feb 5, 2025
bd0b538
Remove addr_space from globals to clean it up a bit
bcardosolopes Feb 5, 2025
7c293b2
Fix newline
bcardosolopes Feb 5, 2025
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
86 changes: 86 additions & 0 deletions mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,92 @@ def LLVM_GlobalDtorsOp : LLVM_Op<"mlir.global_dtors", [
let hasVerifier = 1;
}

def LLVM_AliasOp : LLVM_Op<"mlir.alias",
[IsolatedFromAbove, SingleBlockImplicitTerminator<"ReturnOp">, Symbol]> {
let arguments = (ins
TypeAttr:$alias_type,
StrAttr:$sym_name,
Linkage:$linkage,
UnitAttr:$dso_local,
UnitAttr:$thread_local_,
UnitAttr:$externally_initialized,
DefaultValuedAttr<ConfinedAttr<I32Attr, [IntNonNegative]>, "0">:$addr_space,
OptionalAttr<UnnamedAddr>:$unnamed_addr,
OptionalAttr<StrAttr>:$section,
OptionalAttr<SymbolRefAttr>:$comdat,
DefaultValuedAttr<Visibility, "mlir::LLVM::Visibility::Default">:$visibility_
);
let summary = "LLVM dialect alias.";
let description = [{
`llvm.mlir.alias` is a top level operation that defines a global alias for
global variables and functions. The operation is always initialized by
using a initializer region which could be a direct map to another global
value or contain some address computation on top of it.

It uses an @-identifier for its value, which will be uniqued by the module
with respect to other @-identifiers in it.

Similarly to functions and globals, they can also have a linkage attribute.
This attribute is placed between `llvm.mlir.alias` and the symbol name. If
the attribute is omitted, `external` linkage is assumed by default.

Examples:

```mlir
// Global alias use @-identifiers.
llvm.mlir.alias external @foo_alias {addr_space = 0 : i32} : !llvm.ptr {
%0 = llvm.mlir.addressof @some_function : !llvm.ptr
llvm.return %0 : !llvm.ptr
}

// More complex initialization.
llvm.mlir.alias linkonce_odr hidden @glob
{addr_space = 0 : i32, dso_local} : !llvm.array<32 x i32> {
%0 = llvm.mlir.constant(1234 : i64) : i64
%1 = llvm.mlir.addressof @glob.private : !llvm.ptr
%2 = llvm.ptrtoint %1 : !llvm.ptr to i64
%3 = llvm.add %2, %0 : i64
%4 = llvm.inttoptr %3 : i64 to !llvm.ptr
llvm.return %4 : !llvm.ptr
}
```
}];
let regions = (region AnyRegion:$initializer);

let builders = [
OpBuilder<(ins "Type":$type, "Linkage":$linkage,
"StringRef":$name,
CArg<"unsigned", "0">:$addrSpace,
CArg<"bool", "false">:$dsoLocal,
CArg<"bool", "false">:$thread_local_,
CArg<"SymbolRefAttr", "{}">:$comdat,
CArg<"ArrayRef<NamedAttribute>", "{}">:$attrs,
CArg<"ArrayRef<Attribute>", "{}">:$dbgExprs)>
];

let extraClassDeclaration = [{
/// Return the LLVM type of the global alias.
Type getType() {
return getAliasType();
}
/// Return the initializer region. This may be empty, but if it is not it
/// terminates in an `llvm.return` op with the initializer value.
Region &getInitializerRegion() {
return getOperation()->getRegion(0);
}
/// Return the initializer block. The initializer region always exist
/// (differently from llvm.global) and it terminates with an `llvm.return`
/// op with the aliasee value.
Block *getInitializerBlock() {
return &getInitializerRegion().front();
}
}];

let hasCustomAssemblyFormat = 1;
let hasVerifier = 1;
let hasRegionVerifier = 1;
}

def LLVM_ComdatSelectorOp : LLVM_Op<"comdat_selector", [Symbol]> {
let arguments = (ins
SymbolNameAttr:$sym_name,
Expand Down
8 changes: 8 additions & 0 deletions mlir/include/mlir/Target/LLVMIR/ModuleImport.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ class ModuleImport {
/// Converts all global variables of the LLVM module to MLIR global variables.
LogicalResult convertGlobals();

/// Converts all aliases of the LLVM module to MLIR variables.
LogicalResult convertAliases();

/// Converts the data layout of the LLVM module to an MLIR data layout
/// specification.
LogicalResult convertDataLayout();
Expand Down Expand Up @@ -288,6 +291,9 @@ class ModuleImport {
LogicalResult convertGlobal(llvm::GlobalVariable *globalVar);
/// Imports the magic globals "global_ctors" and "global_dtors".
LogicalResult convertGlobalCtorsAndDtors(llvm::GlobalVariable *globalVar);
/// Converts an LLVM global alias variable into an MLIR LLVM dialect alias
/// operation if a conversion exists. Otherwise, returns failure.
LogicalResult convertAlias(llvm::GlobalAlias *alias);
/// Returns personality of `func` as a FlatSymbolRefAttr.
FlatSymbolRefAttr getPersonalityAsAttr(llvm::Function *func);
/// Imports `bb` into `block`, which must be initially empty.
Expand Down Expand Up @@ -406,6 +412,8 @@ class ModuleImport {
Operation *constantInsertionOp = nullptr;
/// Operation to insert the next global after.
Operation *globalInsertionOp = nullptr;
/// Operation to insert the next alias after.
Operation *aliasInsertionOp = nullptr;
/// Operation to insert comdat selector operations into.
ComdatOp globalComdatOp = nullptr;
/// The current context.
Expand Down
11 changes: 11 additions & 0 deletions mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ class ModuleTranslation {
return globalsMapping.lookup(op);
}

/// Finds an LLVM IR global value that corresponds to the given MLIR operation
/// defining a global alias value.
llvm::GlobalValue *lookupAlias(Operation *op) {
return aliasesMapping.lookup(op);
}

/// Returns the OpenMP IR builder associated with the LLVM IR module being
/// constructed.
llvm::OpenMPIRBuilder *getOpenMPBuilder();
Expand Down Expand Up @@ -322,6 +328,7 @@ class ModuleTranslation {
LogicalResult convertFunctions();
LogicalResult convertComdats();
LogicalResult convertGlobals();
LogicalResult convertAliases();
LogicalResult convertOneFunction(LLVMFuncOp func);
LogicalResult convertBlockImpl(Block &bb, bool ignoreArguments,
llvm::IRBuilderBase &builder,
Expand Down Expand Up @@ -366,6 +373,10 @@ class ModuleTranslation {
/// Mappings between llvm.mlir.global definitions and corresponding globals.
DenseMap<Operation *, llvm::GlobalValue *> globalsMapping;

/// Mappings between llvm.mlir.alias definitions and corresponding global
/// aliases.
DenseMap<Operation *, llvm::GlobalValue *> aliasesMapping;

/// A stateful object used to translate types.
TypeToLLVMIRTranslator typeTranslator;

Expand Down
172 changes: 172 additions & 0 deletions mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2422,6 +2422,178 @@ LogicalResult GlobalDtorsOp::verify() {
return success();
}

//===----------------------------------------------------------------------===//
// Builder, printer and verifier for LLVM::GlobalOp.
//===----------------------------------------------------------------------===//

void AliasOp::build(OpBuilder &builder, OperationState &result, Type type,
Linkage linkage, StringRef name, unsigned addrSpace,
bool dsoLocal, bool threadLocal, SymbolRefAttr comdat,
ArrayRef<NamedAttribute> attrs,
ArrayRef<Attribute> dbgExprs) {
result.addAttribute(getSymNameAttrName(result.name),
builder.getStringAttr(name));
result.addAttribute(getAliasTypeAttrName(result.name), TypeAttr::get(type));
if (dsoLocal)
result.addAttribute(getDsoLocalAttrName(result.name),
builder.getUnitAttr());
if (threadLocal)
result.addAttribute(getThreadLocal_AttrName(result.name),
builder.getUnitAttr());
if (comdat)
result.addAttribute(getComdatAttrName(result.name), comdat);

result.addAttribute(getLinkageAttrName(result.name),
LinkageAttr::get(builder.getContext(), linkage));
if (addrSpace != 0)
result.addAttribute(getAddrSpaceAttrName(result.name),
builder.getI32IntegerAttr(addrSpace));
result.attributes.append(attrs.begin(), attrs.end());

result.addRegion();
}

void AliasOp::print(OpAsmPrinter &p) {
p << ' ' << stringifyLinkage(getLinkage()) << ' ';
StringRef visibility = stringifyVisibility(getVisibility_());
if (!visibility.empty())
p << visibility << ' ';
if (getThreadLocal_())
p << "thread_local ";
if (auto unnamedAddr = getUnnamedAddr()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Do not use auto when the type isn't given on the RHS.

StringRef str = stringifyUnnamedAddr(*unnamedAddr);
if (!str.empty())
p << str << ' ';
}
p.printSymbolName(getSymName());
if (auto comdat = getComdat())
p << " comdat(" << *comdat << ')';

// Note that the alignment attribute is printed using the
// default syntax here, even though it is an inherent attribute
// (as defined in https://mlir.llvm.org/docs/LangRef/#attributes)
p.printOptionalAttrDict((*this)->getAttrs(),
{SymbolTable::getSymbolAttrName(),
getAliasTypeAttrName(), getLinkageAttrName(),
getUnnamedAddrAttrName(), getThreadLocal_AttrName(),
getVisibility_AttrName(), getComdatAttrName(),
getUnnamedAddrAttrName()});

// Print the trailing type
p << " : " << getType();

Region &initializer = getInitializerRegion();
if (!initializer.empty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The region being empty should not be possible.

p << ' ';
p.printRegion(initializer, /*printEntryBlockArgs=*/false);
}
}

// operation ::= `llvm.mlir.alias` linkage? visibility?
// (`unnamed_addr` | `local_unnamed_addr`)?
// `thread_local`? `@` identifier
// `(` attribute? `)` (`comdat(` symbol-ref-id `)`)?
// attribute-list? (`:` type)? region
//
// The type can be omitted for string attributes, in which case it will be
// inferred from the value of the string as [strlen(value) x i8].
ParseResult AliasOp::parse(OpAsmParser &parser, OperationState &result) {
MLIRContext *ctx = parser.getContext();
// Parse optional linkage, default to External.
result.addAttribute(getLinkageAttrName(result.name),
LLVM::LinkageAttr::get(
ctx, parseOptionalLLVMKeyword<Linkage>(
parser, result, LLVM::Linkage::External)));

// Parse optional visibility, default to Default.
result.addAttribute(getVisibility_AttrName(result.name),
parser.getBuilder().getI64IntegerAttr(
parseOptionalLLVMKeyword<LLVM::Visibility, int64_t>(
parser, result, LLVM::Visibility::Default)));

// Parse optional UnnamedAddr, default to None.
result.addAttribute(getUnnamedAddrAttrName(result.name),
parser.getBuilder().getI64IntegerAttr(
parseOptionalLLVMKeyword<UnnamedAddr, int64_t>(
parser, result, LLVM::UnnamedAddr::None)));

if (succeeded(parser.parseOptionalKeyword("thread_local")))
result.addAttribute(getThreadLocal_AttrName(result.name),
parser.getBuilder().getUnitAttr());

StringAttr name;
if (parser.parseSymbolName(name, getSymNameAttrName(result.name),
result.attributes))
return failure();

if (succeeded(parser.parseOptionalKeyword("comdat"))) {
SymbolRefAttr comdat;
if (parser.parseLParen() || parser.parseAttribute(comdat) ||
parser.parseRParen())
return failure();

result.addAttribute(getComdatAttrName(result.name), comdat);
}

SmallVector<Type, 1> types;
if (parser.parseOptionalAttrDict(result.attributes) ||
parser.parseOptionalColonTypeList(types))
return failure();

if (types.size() > 1)
return parser.emitError(parser.getNameLoc(), "expected zero or one type");

Region &initRegion = *result.addRegion();
if (parser.parseRegion(initRegion).failed())
return failure();

result.addAttribute(getAliasTypeAttrName(result.name),
TypeAttr::get(types[0]));
return success();
}

LogicalResult AliasOp::verify() {
bool validType = isCompatibleOuterType(getType())
? !llvm::isa<LLVMVoidType, LLVMTokenType,
LLVMMetadataType, LLVMLabelType>(getType())
: llvm::isa<PointerElementTypeInterface>(getType());
if (!validType)
return emitOpError(
"expects type to be a valid element type for an LLVM global");
if ((*this)->getParentOp() && !satisfiesLLVMModule((*this)->getParentOp()))
return emitOpError("must appear at the module level");

if (getLinkage() == Linkage::Appending) {
if (!llvm::isa<LLVMArrayType>(getType())) {
return emitOpError() << "expected array type for '"
<< stringifyLinkage(Linkage::Appending)
<< "' linkage";
}
}

if (failed(verifyComdat(*this, getComdat())))
return failure();

return success();
}

LogicalResult AliasOp::verifyRegions() {
if (Block *b = getInitializerBlock()) {
ReturnOp ret = cast<ReturnOp>(b->getTerminator());
if (ret.operand_type_begin() == ret.operand_type_end())
return emitOpError("initializer region cannot return void");

for (Operation &op : *b) {
auto iface = dyn_cast<MemoryEffectOpInterface>(op);
if (!iface || !iface.hasNoEffect())
return op.emitError()
<< "ops with side effects not allowed in aliases initializers";
}
}

return success();
}

//===----------------------------------------------------------------------===//
// ShuffleVectorOp
//===----------------------------------------------------------------------===//
Expand Down
Loading