Skip to content

Commit 8a2a694

Browse files
[MLIR][LLVM] Support dso_local_equivalent constants (#132131)
Create a new operation `DSOLocalEquivalentOp`, following the steps of other constants. This is similar in a way to `AddressOfOp` but with specific semantics: only support functions and function aliases (no globals) and extern_weak linkage is not allowed. An alternative approach is to use a new `UnitAttr` in `AddressOfOp` and check that attribute to enforce specific semantics in the verifiers. The drawback is going against what other constants do and having to add more attributes in the future when we introduce `no_cfi`, `blockaddress`, etc. While here, improve the error message for other missing constants.
1 parent e75f586 commit 8a2a694

File tree

9 files changed

+289
-0
lines changed

9 files changed

+289
-0
lines changed

mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,17 @@ def LLVM_UndefAttr : LLVM_Attr<"Undef", "undef">;
12131213
/// Folded into from LLVM::PoisonOp.
12141214
def LLVM_PoisonAttr : LLVM_Attr<"Poison", "poison">;
12151215

1216+
//===----------------------------------------------------------------------===//
1217+
// DSOLocalEquivalentAttr
1218+
//===----------------------------------------------------------------------===//
1219+
1220+
/// Folded into from LLVM::DSOLocalEquivalentOp.
1221+
def LLVM_DSOLocalEquivalentAttr : LLVM_Attr<"DSOLocalEquivalent",
1222+
"dso_local_equivalent"> {
1223+
let parameters = (ins "FlatSymbolRefAttr":$sym);
1224+
let assemblyFormat = "$sym";
1225+
}
1226+
12161227
//===----------------------------------------------------------------------===//
12171228
// VecTypeHintAttr
12181229
//===----------------------------------------------------------------------===//

mlir/include/mlir/Dialect/LLVMIR/LLVMOps.td

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,44 @@ def LLVM_AliasOp : LLVM_Op<"mlir.alias",
15751575
let hasRegionVerifier = 1;
15761576
}
15771577

1578+
def LLVM_DSOLocalEquivalentOp : LLVM_Op<"dso_local_equivalent",
1579+
[Pure, ConstantLike, DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
1580+
let arguments = (ins FlatSymbolRefAttr:$function_name);
1581+
let results = (outs LLVM_AnyPointer:$res);
1582+
1583+
let summary = "Creates a LLVM dso_local_equivalent ptr";
1584+
1585+
let description = [{
1586+
Creates an SSA value containing a pointer to a global value (function or
1587+
alias to function). It represents a function which is functionally
1588+
equivalent to a given function, but is always defined in the current
1589+
linkage unit. The target function may not have `extern_weak` linkage.
1590+
1591+
Examples:
1592+
1593+
```mlir
1594+
llvm.mlir.global external constant @const() : i64 {
1595+
%0 = llvm.mlir.addressof @const : !llvm.ptr
1596+
%1 = llvm.ptrtoint %0 : !llvm.ptr to i64
1597+
%2 = llvm.dso_local_equivalent @func : !llvm.ptr
1598+
%4 = llvm.ptrtoint %2 : !llvm.ptr to i64
1599+
llvm.return %4 : i64
1600+
}
1601+
```
1602+
}];
1603+
1604+
let extraClassDeclaration = [{
1605+
/// Return the llvm.func operation that is referenced here.
1606+
LLVMFuncOp getFunction(SymbolTableCollection &symbolTable);
1607+
/// Return the llvm.mlir.alias operation that defined the value referenced
1608+
/// here.
1609+
AliasOp getAlias(SymbolTableCollection &symbolTable);
1610+
}];
1611+
1612+
let assemblyFormat = "$function_name attr-dict `:` qualified(type($res))";
1613+
let hasFolder = 1;
1614+
}
1615+
15781616
def LLVM_ComdatSelectorOp : LLVM_Op<"comdat_selector", [Symbol]> {
15791617
let arguments = (ins
15801618
SymbolNameAttr:$sym_name,

mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,6 +2122,57 @@ OpFoldResult LLVM::AddressOfOp::fold(FoldAdaptor) {
21222122
return getGlobalNameAttr();
21232123
}
21242124

2125+
//===----------------------------------------------------------------------===//
2126+
// LLVM::DSOLocalEquivalentOp
2127+
//===----------------------------------------------------------------------===//
2128+
2129+
LLVMFuncOp
2130+
DSOLocalEquivalentOp::getFunction(SymbolTableCollection &symbolTable) {
2131+
return dyn_cast_or_null<LLVMFuncOp>(symbolTable.lookupSymbolIn(
2132+
parentLLVMModule(*this), getFunctionNameAttr()));
2133+
}
2134+
2135+
AliasOp DSOLocalEquivalentOp::getAlias(SymbolTableCollection &symbolTable) {
2136+
return dyn_cast_or_null<AliasOp>(symbolTable.lookupSymbolIn(
2137+
parentLLVMModule(*this), getFunctionNameAttr()));
2138+
}
2139+
2140+
LogicalResult
2141+
DSOLocalEquivalentOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
2142+
Operation *symbol = symbolTable.lookupSymbolIn(parentLLVMModule(*this),
2143+
getFunctionNameAttr());
2144+
auto function = dyn_cast_or_null<LLVMFuncOp>(symbol);
2145+
auto alias = dyn_cast_or_null<AliasOp>(symbol);
2146+
2147+
if (!function && !alias)
2148+
return emitOpError(
2149+
"must reference a global defined by 'llvm.func' or 'llvm.mlir.alias'");
2150+
2151+
if (alias) {
2152+
if (alias.getInitializer()
2153+
.walk([&](AddressOfOp addrOp) {
2154+
if (addrOp.getGlobal(symbolTable))
2155+
return WalkResult::interrupt();
2156+
return WalkResult::advance();
2157+
})
2158+
.wasInterrupted())
2159+
return emitOpError("must reference an alias to a function");
2160+
}
2161+
2162+
if ((function && function.getLinkage() == LLVM::Linkage::ExternWeak) ||
2163+
(alias && alias.getLinkage() == LLVM::Linkage::ExternWeak))
2164+
return emitOpError(
2165+
"target function with 'extern_weak' linkage not allowed");
2166+
2167+
return success();
2168+
}
2169+
2170+
/// Fold a dso_local_equivalent operation to a dedicated dso_local_equivalent
2171+
/// attribute.
2172+
OpFoldResult DSOLocalEquivalentOp::fold(FoldAdaptor) {
2173+
return DSOLocalEquivalentAttr::get(getContext(), getFunctionNameAttr());
2174+
}
2175+
21252176
//===----------------------------------------------------------------------===//
21262177
// Verifier for LLVM::ComdatOp.
21272178
//===----------------------------------------------------------------------===//

mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,31 @@ convertOperationImpl(Operation &opInst, llvm::IRBuilderBase &builder,
526526
return success();
527527
}
528528

529+
// Emit dso_local_equivalent. We need to look up the global value referenced
530+
// by the operation and store it in the MLIR-to-LLVM value mapping.
531+
if (auto dsoLocalEquivalentOp =
532+
dyn_cast<LLVM::DSOLocalEquivalentOp>(opInst)) {
533+
LLVM::LLVMFuncOp function =
534+
dsoLocalEquivalentOp.getFunction(moduleTranslation.symbolTable());
535+
LLVM::AliasOp alias =
536+
dsoLocalEquivalentOp.getAlias(moduleTranslation.symbolTable());
537+
538+
// The verifier should not have allowed this.
539+
assert((function || alias) &&
540+
"referencing an undefined function, or alias");
541+
542+
llvm::Value *llvmValue = nullptr;
543+
if (alias)
544+
llvmValue = moduleTranslation.lookupAlias(alias);
545+
else
546+
llvmValue = moduleTranslation.lookupFunction(function.getName());
547+
548+
moduleTranslation.mapValue(
549+
dsoLocalEquivalentOp.getResult(),
550+
llvm::DSOLocalEquivalent::get(cast<llvm::GlobalValue>(llvmValue)));
551+
return success();
552+
}
553+
529554
return failure();
530555
}
531556

mlir/lib/Target/LLVMIR/ModuleImport.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,18 @@ FailureOr<Value> ModuleImport::convertConstant(llvm::Constant *constant) {
12481248
return builder.create<UndefOp>(loc, type).getResult();
12491249
}
12501250

1251+
// Convert dso_local_equivalent.
1252+
if (auto *dsoLocalEquivalent = dyn_cast<llvm::DSOLocalEquivalent>(constant)) {
1253+
Type type = convertType(dsoLocalEquivalent->getType());
1254+
return builder
1255+
.create<DSOLocalEquivalentOp>(
1256+
loc, type,
1257+
FlatSymbolRefAttr::get(
1258+
builder.getContext(),
1259+
dsoLocalEquivalent->getGlobalValue()->getName()))
1260+
.getResult();
1261+
}
1262+
12511263
// Convert global variable accesses.
12521264
if (auto *globalObj = dyn_cast<llvm::GlobalObject>(constant)) {
12531265
Type type = convertType(globalObj->getType());
@@ -1347,6 +1359,15 @@ FailureOr<Value> ModuleImport::convertConstant(llvm::Constant *constant) {
13471359
if (isa<llvm::BlockAddress>(constant))
13481360
error = " since blockaddress(...) is unsupported";
13491361

1362+
if (isa<llvm::ConstantPtrAuth>(constant))
1363+
error = " since ptrauth(...) is unsupported";
1364+
1365+
if (isa<llvm::NoCFIValue>(constant))
1366+
error = " since no_cfi is unsupported";
1367+
1368+
if (isa<llvm::GlobalValue>(constant))
1369+
error = " since global value is unsupported";
1370+
13501371
return emitError(loc) << "unhandled constant: " << diag(*constant) << error;
13511372
}
13521373

mlir/test/Dialect/LLVMIR/constant-folding.mlir

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,17 @@ func.func @insert_op(%arg0: index, %arg1: memref<13x13xi64>, %arg2: index) {
182182
vector.print %101 : vector<1xi64>
183183
return
184184
}
185+
186+
// -----
187+
188+
// CHECK-LABEL: llvm.func @dso_local_equivalent_select
189+
llvm.func @dso_local_equivalent_select(%arg: i1) -> !llvm.ptr {
190+
// CHECK-NEXT: %[[DSOLOCALEQ:.+]] = llvm.dso_local_equivalent @yay
191+
%0 = llvm.dso_local_equivalent @yay : !llvm.ptr
192+
%1 = llvm.dso_local_equivalent @yay : !llvm.ptr
193+
%2 = arith.select %arg, %0, %1 : !llvm.ptr
194+
// CHECK-NEXT: llvm.return %[[DSOLOCALEQ]]
195+
llvm.return %2 : !llvm.ptr
196+
}
197+
198+
llvm.func @yay()

mlir/test/Target/LLVMIR/Import/constant.ll

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,41 @@ define i64 @const_exprs_with_duplicate() {
236236
; CHECK: %[[VAL0:.+]] = llvm.ptrtoint %[[ADDR]]
237237
; CHECK: %[[VAL1:.+]] = llvm.add %[[VAL0]], %[[VAL0]]
238238
; CHECK: llvm.return %[[VAL1]]
239+
240+
; // -----
241+
242+
declare void @extern_func()
243+
@const = dso_local constant i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @extern_func to i64), i64 ptrtoint (ptr @const to i64)) to i32)
244+
245+
; CHECK: llvm.mlir.global external constant @const()
246+
; CHECK: %[[ADDR:.+]] = llvm.mlir.addressof @const : !llvm.ptr
247+
; CHECK: llvm.ptrtoint %[[ADDR]] : !llvm.ptr to i64
248+
; CHECK: llvm.dso_local_equivalent @extern_func : !llvm.ptr
249+
250+
; // -----
251+
252+
declare i32 @extern_func()
253+
254+
define void @call_extern_func() {
255+
call noundef i32 dso_local_equivalent @extern_func()
256+
ret void
257+
}
258+
259+
; CHECK-LABEL: @call_extern_func()
260+
; CHECK: %[[DSO_EQ:.+]] = llvm.dso_local_equivalent @extern_func : !llvm.ptr
261+
; CHECK: llvm.call %[[DSO_EQ]]() : !llvm.ptr, () -> (i32 {llvm.noundef})
262+
263+
; // -----
264+
265+
define void @aliasee_func() {
266+
ret void
267+
}
268+
269+
@alias_func = alias void (), ptr @aliasee_func
270+
define void @call_alias_func() {
271+
call void dso_local_equivalent @alias_func()
272+
ret void
273+
}
274+
275+
; CHECK-LABEL: @call_alias_func()
276+
; CHECK: llvm.dso_local_equivalent @alias_func : !llvm.ptr

mlir/test/Target/LLVMIR/llvmir-invalid.mlir

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,45 @@ module @no_known_conversion_innermost_eltype {
411411
}
412412
}
413413
#-}
414+
415+
// -----
416+
417+
llvm.mlir.global external @zed(42 : i32) : i32
418+
419+
llvm.mlir.alias external @foo : i32 {
420+
%0 = llvm.mlir.addressof @zed : !llvm.ptr
421+
llvm.return %0 : !llvm.ptr
422+
}
423+
424+
llvm.func @call_alias_func() {
425+
// expected-error @below{{'llvm.dso_local_equivalent' op must reference an alias to a function}}
426+
%0 = llvm.dso_local_equivalent @foo : !llvm.ptr
427+
llvm.call %0() : !llvm.ptr, () -> (i32)
428+
llvm.return
429+
}
430+
431+
// -----
432+
433+
llvm.mlir.global external @y() : !llvm.ptr
434+
435+
llvm.func @call_alias_func() {
436+
// expected-error @below{{op must reference a global defined by 'llvm.func' or 'llvm.mlir.alias'}}
437+
%0 = llvm.dso_local_equivalent @y : !llvm.ptr
438+
llvm.call %0() : !llvm.ptr, () -> (i32)
439+
llvm.return
440+
}
441+
442+
// -----
443+
444+
llvm.mlir.global external constant @const() {addr_space = 0 : i32, dso_local} : i32 {
445+
%0 = llvm.mlir.addressof @const : !llvm.ptr
446+
%1 = llvm.ptrtoint %0 : !llvm.ptr to i64
447+
// expected-error @below{{'llvm.dso_local_equivalent' op target function with 'extern_weak' linkage not allowed}}
448+
%2 = llvm.dso_local_equivalent @extern_func : !llvm.ptr
449+
%3 = llvm.ptrtoint %2 : !llvm.ptr to i64
450+
%4 = llvm.sub %3, %1 : i64
451+
%5 = llvm.trunc %4 : i64 to i32
452+
llvm.return %5 : i32
453+
}
454+
455+
llvm.func extern_weak @extern_func()

mlir/test/Target/LLVMIR/llvmir.mlir

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2793,3 +2793,52 @@ module {
27932793

27942794
// CHECK: !llvm.module.flags = !{![[#DBG:]]}
27952795
// CHECK: ![[#DBG]] = !{i32 2, !"Debug Info Version", i32 3}
2796+
2797+
// -----
2798+
2799+
llvm.mlir.global external constant @const() {addr_space = 0 : i32, dso_local} : i32 {
2800+
%0 = llvm.mlir.addressof @const : !llvm.ptr
2801+
%1 = llvm.ptrtoint %0 : !llvm.ptr to i64
2802+
%2 = llvm.dso_local_equivalent @extern_func : !llvm.ptr
2803+
%3 = llvm.ptrtoint %2 : !llvm.ptr to i64
2804+
%4 = llvm.sub %3, %1 : i64
2805+
%5 = llvm.trunc %4 : i64 to i32
2806+
llvm.return %5 : i32
2807+
}
2808+
2809+
llvm.func @extern_func()
2810+
2811+
// CHECK: @const = dso_local constant i32 trunc
2812+
// CHECK-SAME: (i64 sub (i64 ptrtoint
2813+
// CHECK-SAME: (ptr dso_local_equivalent @extern_func to i64),
2814+
// CHECK-SAME: i64 ptrtoint (ptr @const to i64)) to i32)
2815+
2816+
// -----
2817+
2818+
llvm.func @extern_func() -> i32
2819+
llvm.func @call_extern_func() {
2820+
%0 = llvm.dso_local_equivalent @extern_func : !llvm.ptr
2821+
%1 = llvm.call %0() : !llvm.ptr, () -> (i32 {llvm.noundef})
2822+
llvm.return
2823+
}
2824+
2825+
// CHECK-LABEL: @call_extern_func()
2826+
// CHECK: call noundef i32 dso_local_equivalent @extern_func()
2827+
2828+
// -----
2829+
2830+
llvm.mlir.alias external @alias_func : !llvm.func<void ()> {
2831+
%0 = llvm.mlir.addressof @aliasee_func : !llvm.ptr
2832+
llvm.return %0 : !llvm.ptr
2833+
}
2834+
llvm.func @aliasee_func() {
2835+
llvm.return
2836+
}
2837+
llvm.func @call_alias_func() {
2838+
%0 = llvm.dso_local_equivalent @alias_func : !llvm.ptr
2839+
llvm.call %0() : !llvm.ptr, () -> ()
2840+
llvm.return
2841+
}
2842+
2843+
// CHECK-LABEL: @call_alias_func
2844+
// CHECK: call void dso_local_equivalent @alias_func()

0 commit comments

Comments
 (0)