Skip to content

Add support for unnamed_attr resolving in linker #31

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

Merged
merged 3 commits into from
Apr 18, 2025
Merged
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
9 changes: 9 additions & 0 deletions clang/lib/CIR/Interfaces/CIRLinkerInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ class CIRSymbolLinkerInterface
}

static unsigned getBitWidth(Operation *op) { llvm_unreachable("NYI"); }

// FIXME: CIR does not yet have UnnamedAddr attribute
static UnnamedAddr getUnnamedAddr(Operation */* op*/) {
return UnnamedAddr::Global;
}

// FIXME: CIR does not yet have UnnamedAddr attribute
static void setUnnamedAddr(Operation */* op*/, UnnamedAddr addr) {}

};

//===----------------------------------------------------------------------===//
Expand Down
54 changes: 54 additions & 0 deletions mlir/include/mlir/Linker/LLVMLinkerMixin.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,28 @@ static inline Visibility getMinVisibility(Visibility lhs, Visibility rhs) {
return Visibility::Default;
}

//===----------------------------------------------------------------------===//
// Unnamed_addr helpers
//===----------------------------------------------------------------------===//

using UnnamedAddr = LLVM::UnnamedAddr;

static bool isNoneUnnamedAddr(UnnamedAddr val) {
return val == UnnamedAddr::None;
}

static bool isLocalUnnamedAddr(UnnamedAddr val) {
return val == UnnamedAddr::Local;
}

static UnnamedAddr getMinUnnamedAddr(UnnamedAddr lhs, UnnamedAddr rhs) {
if (isNoneUnnamedAddr(lhs) || isNoneUnnamedAddr(rhs))
return UnnamedAddr::None;
if (isLocalUnnamedAddr(lhs) || isLocalUnnamedAddr(rhs))
return UnnamedAddr::Local;
return UnnamedAddr::Global;
}

//===----------------------------------------------------------------------===//
// LLVMLinkerMixin
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -176,6 +198,29 @@ class LLVMLinkerMixin {
isAvailableExternallyLinkage(srcLinkage));
}

LogicalResult verifyLinkageCompatibility(Conflict pair) {
const DerivedLinkerInterface &derived = getDerived();
assert(derived.canBeLinked(pair.src) && "expected linkable operation");
assert(derived.canBeLinked(pair.dst) && "expected linkable operation");

auto linkError = [&](const Twine &error) -> LogicalResult {
return pair.src->emitError(error) << " dst: " << pair.dst->getLoc();
};

Linkage srcLinkage = derived.getLinkage(pair.src);
Linkage dstLinkage = derived.getLinkage(pair.dst);

UnnamedAddr srcUnnamedAddr = derived.getUnnamedAddr(pair.src);
UnnamedAddr dstUnnamedAddr = derived.getUnnamedAddr(pair.dst);

if (isAppendingLinkage(srcLinkage) && isAppendingLinkage(dstLinkage)) {
if (srcUnnamedAddr != dstUnnamedAddr) {
return linkError("Appending variables with different unnamed_addr need to be linked");
}
}
return success();
}

ConflictResolution resolveConflict(Conflict pair) {
const DerivedLinkerInterface &derived = getDerived();
assert(derived.canBeLinked(pair.src) && "expected linkable operation");
Expand All @@ -191,6 +236,13 @@ class LLVMLinkerMixin {
derived.setVisibility(pair.src, visibility);
derived.setVisibility(pair.dst, visibility);

UnnamedAddr srcUnnamedAddr = derived.getUnnamedAddr(pair.src);
UnnamedAddr dstUnnamedAddr = derived.getUnnamedAddr(pair.dst);

UnnamedAddr unnamedAddr = getMinUnnamedAddr(srcUnnamedAddr, dstUnnamedAddr);
derived.setUnnamedAddr(pair.src, unnamedAddr);
derived.setUnnamedAddr(pair.dst, unnamedAddr);

const bool srcIsDeclaration = isDeclarationForLinker(pair.src);
const bool dstIsDeclaration = isDeclarationForLinker(pair.dst);

Expand Down Expand Up @@ -267,6 +319,8 @@ class SymbolAttrLLVMLinkerInterface
}

LogicalResult resolveConflict(Conflict pair) override {
if (failed(LinkerMixin::verifyLinkageCompatibility(pair)))
return failure();
ConflictResolution resolution = LinkerMixin::resolveConflict(pair);

switch (resolution) {
Expand Down
20 changes: 20 additions & 0 deletions mlir/lib/Dialect/LLVMIR/IR/LLVMLinkerInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,26 @@ class LLVMSymbolLinkerInterface
return gv.getType().getIntOrFloatBitWidth();
llvm_unreachable("unexpected operation");
}

static UnnamedAddr getUnnamedAddr(Operation *op) {
if (auto gv = dyn_cast<LLVM::GlobalOp>(op)) {
auto addr = gv.getUnnamedAddr();
return addr ? *addr : UnnamedAddr::Global;
}
if (auto fn = dyn_cast<LLVM::LLVMFuncOp>(op)) {
auto addr = fn.getUnnamedAddr();
return addr ? *addr : UnnamedAddr::Global;
}
llvm_unreachable("unexpected operation");
}

static void setUnnamedAddr(Operation *op, UnnamedAddr val) {
if (auto gv = dyn_cast<LLVM::GlobalOp>(op))
return gv.setUnnamedAddr(val);
if (auto fn = dyn_cast<LLVM::LLVMFuncOp>(op))
return fn.setUnnamedAddr(val);
llvm_unreachable("unexpected operation");
}
};

//===----------------------------------------------------------------------===//
Expand Down
7 changes: 7 additions & 0 deletions mlir/test/mlir-link/adapted/unnamed-addr-err-a.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// RUN: not mlir-link %s %S/unnamed-addr-err-b.mlir 2>&1 | FileCheck %s

// CHECK: Appending variables with different unnamed_addr need to be linked

module {
llvm.mlir.global appending unnamed_addr @foo(dense<42> : tensor<1xi32>) {addr_space = 0 : i32} : !llvm.array<1 x i32>
}
5 changes: 5 additions & 0 deletions mlir/test/mlir-link/adapted/unnamed-addr-err-b.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// RUN: true

module {
llvm.mlir.global appending @foo(dense<42> : tensor<1xi32>) {addr_space = 0 : i32} : !llvm.array<1 x i32>
}
94 changes: 94 additions & 0 deletions mlir/test/mlir-link/adapted/unnamed-addr1-a.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// RUN: mlir-link %s %S/unnamed-addr1-b.ll -o - | FileCheck %s

// llvm.mlir.alias not yet supported
// XFAIL: *

module {
// CHECK-DAG: llvm.mlir.global common @"global-a"(0 : i32)
llvm.mlir.global common @"global-a"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global common unnamed_addr @"global-b"(0 : i32)
llvm.mlir.global common unnamed_addr @"global-b"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global common unnamed_addr @"global-c"(0 : i32)
llvm.mlir.global common unnamed_addr @"global-c"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-d"(42 : i32)
llvm.mlir.global external @"global-d"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external unnamed_addr @"global-e"(42 : i32)
llvm.mlir.global external unnamed_addr @"global-e"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-f"(42 : i32)
llvm.mlir.global weak @"global-f"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.alias external @"alias-a" : i32 {
llvm.mlir.global weak @"alias-a"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.alias external unnamed_addr @"alias-b" : i32 {
llvm.mlir.global weak unnamed_addr @"alias-b"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global common @"global-g"(0: i32)
llvm.mlir.global common unnamed_addr @"global-g"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-h"(42 : i32)
llvm.mlir.global external @"global-h"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-i"(42 : i32)
llvm.mlir.global external unnamed_addr @"global-i"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-j"(42 : i32)
llvm.mlir.global weak @"global-j"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.alias external @"alias-c" : i32 {
llvm.mlir.global weak @"alias-c"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.alias external @"alias-d" : i32 {
llvm.mlir.global weak unnamed_addr @"alias-d"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.func weak @"func-a"() {
llvm.func weak @"func-a"() {
llvm.return
}

// CHECK-DAG: llvm.func weak unnamed_addr @"func-b"() {
llvm.func weak unnamed_addr @"func-b"() {
llvm.return
}

llvm.func @"use-global-d"() -> !llvm.ptr {
%0 = llvm.mlir.addressof @"global-d" : !llvm.ptr
llvm.return %0 : !llvm.ptr
}

// CHECK-DAG: llvm.func weak @"func-c"() {
llvm.func @"func-c"()

llvm.func @"use-func-c"() {
llvm.call @"func-c"() : () -> ()
llvm.return
}

// CHECK-DAG: llvm.func weak @"func-d"() {
llvm.func weak @"func-d"() {
llvm.return
}

// CHECK-DAG llvm.func weak unnamed_addr @"func-e"() {
llvm.func weak unnamed_addr @"func-e"() {
llvm.return
}

// CHECK-DAG: llvm.func weak @"func-g"() {
llvm.func @"func-g"()

// CHECK-DAG: llvm.func weak @"func-h"() {
llvm.func weak @"func-h"() {
llvm.return
}

// CHECK-DAG: llvm.func weak @"func-i"() {
llvm.func weak unnamed_addr @"func-i"() {
llvm.return
}
}
46 changes: 46 additions & 0 deletions mlir/test/mlir-link/adapted/unnamed-addr1-b.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// RUN: true

module {
llvm.mlir.global common unnamed_addr @"global-c"(0 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external unnamed_addr @"global-d"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external unnamed_addr @"global-e"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external unnamed_addr @"global-f"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global common @"global-g"(0 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external @"global-h"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external @"global-i"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external @"global-j"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.alias external unnamed_addr @"alias-a" : i32 {
%0 = llvm.mlir.addressof @"global-f" : !llvm.ptr
llvm.return %0 : !llvm.ptr
}
llvm.mlir.alias external unnamed_addr @"alias-b" : i32 {
%0 = llvm.mlir.addressof @"global-f" : !llvm.ptr
llvm.return %0 : !llvm.ptr
}
llvm.mlir.alias external @"alias-c" : i32 {
%0 = llvm.mlir.addressof @"global-f" : !llvm.ptr
llvm.return %0 : !llvm.ptr
}
llvm.mlir.alias external @"alias-d" : i32 {
%0 = llvm.mlir.addressof @"global-f" : !llvm.ptr
llvm.return %0 : !llvm.ptr
}
llvm.func weak unnamed_addr @"func-c"() {
llvm.return
}
llvm.func weak unnamed_addr @"func-d"() {
llvm.return
}
llvm.func weak unnamed_addr @"func-e"() {
llvm.return
}
llvm.func weak @"func-g"() {
llvm.return
}
llvm.func weak @"func-h"() {
llvm.return
}
llvm.func weak @"func-i"() {
llvm.return
}
}
110 changes: 110 additions & 0 deletions mlir/test/mlir-link/unnamed-addr.mlir
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// RUN: mlir-link %s -split-input-file -o - | FileCheck %s

module {
// CHECK-DAG: llvm.mlir.global common @"global-a"(0 : i32)
llvm.mlir.global common @"global-a"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global common unnamed_addr @"global-b"(0 : i32)
llvm.mlir.global common unnamed_addr @"global-b"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global common unnamed_addr @"global-c"(0 : i32)
llvm.mlir.global common unnamed_addr @"global-c"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-d"(42 : i32)
llvm.mlir.global external @"global-d"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external unnamed_addr @"global-e"(42 : i32)
llvm.mlir.global external unnamed_addr @"global-e"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-f"(42 : i32)
llvm.mlir.global weak @"global-f"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global common @"global-g"(0 : i32)
llvm.mlir.global common unnamed_addr @"global-g"(0 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-h"(42 : i32)
llvm.mlir.global external @"global-h"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-i"(42 : i32)
llvm.mlir.global external unnamed_addr @"global-i"() {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.mlir.global external @"global-j"(42 : i32)
llvm.mlir.global weak @"global-j"(42 : i32) {addr_space = 0 : i32} : i32

// CHECK-DAG: llvm.func weak @"func-a"() {
llvm.func weak @"func-a"() {
llvm.return
}

// CHECK-DAG: llvm.func weak unnamed_addr @"func-b"() {
llvm.func weak unnamed_addr @"func-b"() {
llvm.return
}

llvm.func @"use-global-d"() -> !llvm.ptr {
%0 = llvm.mlir.addressof @"global-d" : !llvm.ptr
llvm.return %0 : !llvm.ptr
}

// CHECK-DAG: llvm.func weak @"func-c"() {
llvm.func @"func-c"()

llvm.func @"use-func-c"() {
llvm.call @"func-c"() : () -> ()
llvm.return
}

// CHECK-DAG: llvm.func weak @"func-d"() {
llvm.func weak @"func-d"() {
llvm.return
}

// CHECK-DAG llvm.func weak unnamed_addr @"func-e"() {
llvm.func weak unnamed_addr @"func-e"() {
llvm.return
}

// CHECK-DAG: llvm.func weak @"func-g"() {
llvm.func @"func-g"()

// CHECK-DAG: llvm.func weak @"func-h"() {
llvm.func weak @"func-h"() {
llvm.return
}

// CHECK-DAG: llvm.func weak @"func-i"() {
llvm.func weak unnamed_addr @"func-i"() {
llvm.return
}
}

// -----

module {
llvm.mlir.global common unnamed_addr @"global-c"(0 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external unnamed_addr @"global-d"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external unnamed_addr @"global-e"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external unnamed_addr @"global-f"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global common @"global-g"(0 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external @"global-h"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external @"global-i"(42 : i32) {addr_space = 0 : i32} : i32
llvm.mlir.global external @"global-j"(42 : i32) {addr_space = 0 : i32} : i32
llvm.func weak unnamed_addr @"func-c"() {
llvm.return
}
llvm.func weak unnamed_addr @"func-d"() {
llvm.return
}
llvm.func weak unnamed_addr @"func-e"() {
llvm.return
}
llvm.func weak @"func-g"() {
llvm.return
}
llvm.func weak @"func-h"() {
llvm.return
}
llvm.func weak @"func-i"() {
llvm.return
}
}
Loading