Skip to content

Commit 065d2d9

Browse files
authored
[MLIR][LLVM] Improve the noalias propagation during inlining (llvm#104750)
This commit changes the LLVM dialect's inliner interface to properly propagate noalias information to memory accesses that have different underlying object. By always introducing an SSACopy intrinsic, it's possible to understand that specific memory operations are using unrelated pointers. Previously, the backwards slice walk did continue beyond the boundary of the original function and failed to reason about the "underlying objects".
1 parent 63267ca commit 065d2d9

File tree

2 files changed

+60
-35
lines changed

2 files changed

+60
-35
lines changed

mlir/lib/Dialect/LLVMIR/Transforms/InlinerInterfaceImpl.cpp

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -271,37 +271,41 @@ getUnderlyingObjectSet(Value pointerValue) {
271271
static void createNewAliasScopesFromNoAliasParameter(
272272
Operation *call, iterator_range<Region::iterator> inlinedBlocks) {
273273

274-
// First collect all noalias parameters. These have been specially marked by
275-
// the `handleArgument` implementation by using the `ssa.copy` intrinsic and
276-
// attaching a `noalias` attribute to it.
277-
// These are only meant to be temporary and should therefore be deleted after
278-
// we're done using them here.
274+
// First, collect all ssa copy operations, which correspond to function
275+
// parameters, and additionally store the noalias parameters. All parameters
276+
// have been marked by the `handleArgument` implementation by using the
277+
// `ssa.copy` intrinsic. Additionally, noalias parameters have an attached
278+
// `noalias` attribute to the intrinsics. These intrinsics are only meant to
279+
// be temporary and should therefore be deleted after we're done using them
280+
// here.
281+
SetVector<LLVM::SSACopyOp> ssaCopies;
279282
SetVector<LLVM::SSACopyOp> noAliasParams;
280283
for (Value argument : cast<LLVM::CallOp>(call).getArgOperands()) {
281284
for (Operation *user : argument.getUsers()) {
282285
auto ssaCopy = llvm::dyn_cast<LLVM::SSACopyOp>(user);
283286
if (!ssaCopy)
284287
continue;
288+
ssaCopies.insert(ssaCopy);
289+
285290
if (!ssaCopy->hasAttr(LLVM::LLVMDialect::getNoAliasAttrName()))
286291
continue;
287-
288292
noAliasParams.insert(ssaCopy);
289293
}
290294
}
291295

292-
// If there were none, we have nothing to do here.
293-
if (noAliasParams.empty())
294-
return;
295-
296296
// Scope exit block to make it impossible to forget to get rid of the
297297
// intrinsics.
298298
auto exit = llvm::make_scope_exit([&] {
299-
for (LLVM::SSACopyOp ssaCopyOp : noAliasParams) {
299+
for (LLVM::SSACopyOp ssaCopyOp : ssaCopies) {
300300
ssaCopyOp.replaceAllUsesWith(ssaCopyOp.getOperand());
301301
ssaCopyOp->erase();
302302
}
303303
});
304304

305+
// If there were no noalias parameters, we have nothing to do here.
306+
if (noAliasParams.empty())
307+
return;
308+
305309
// Create a new domain for this specific inlining and a new scope for every
306310
// noalias parameter.
307311
auto functionDomain = LLVM::AliasScopeDomainAttr::get(
@@ -335,7 +339,7 @@ static void createNewAliasScopesFromNoAliasParameter(
335339
bool aliasesOtherKnownObject = false;
336340
// Go through the based on pointers and check that they are either:
337341
// * Constants that can be ignored (undef, poison, null pointer).
338-
// * Based on a noalias parameter.
342+
// * Based on a pointer parameter.
339343
// * Other pointers that we know can't alias with our noalias parameter.
340344
//
341345
// Any other value might be a pointer based on any noalias parameter that
@@ -346,11 +350,13 @@ static void createNewAliasScopesFromNoAliasParameter(
346350
if (matchPattern(object, m_Constant()))
347351
return false;
348352

349-
if (noAliasParams.contains(object.getDefiningOp<LLVM::SSACopyOp>()))
353+
if (auto ssaCopy = object.getDefiningOp<LLVM::SSACopyOp>()) {
354+
// If that value is based on a noalias parameter, it is guaranteed
355+
// to not alias with any other object.
356+
aliasesOtherKnownObject |= !noAliasParams.contains(ssaCopy);
350357
return false;
358+
}
351359

352-
// TODO: This should include other arguments from the inlined
353-
// callable.
354360
if (isa_and_nonnull<LLVM::AllocaOp, LLVM::AddressOfOp>(
355361
object.getDefiningOp())) {
356362
aliasesOtherKnownObject = true;
@@ -773,29 +779,25 @@ struct LLVMInlinerInterface : public DialectInlinerInterface {
773779
return handleByValArgument(builder, callable, argument, elementType,
774780
requestedAlignment);
775781
}
776-
if (argumentAttrs.contains(LLVM::LLVMDialect::getNoAliasAttrName())) {
777-
if (argument.use_empty())
778-
return argument;
779-
780-
// This code is essentially a workaround for deficiencies in the
781-
// inliner interface: We need to transform operations *after* inlined
782-
// based on the argument attributes of the parameters *before* inlining.
783-
// This method runs prior to actual inlining and thus cannot transform the
784-
// post-inlining code, while `processInlinedCallBlocks` does not have
785-
// access to pre-inlining function arguments. Additionally, it is required
786-
// to distinguish which parameter an SSA value originally came from.
787-
// As a workaround until this is changed: Create an ssa.copy intrinsic
788-
// with the noalias attribute that can easily be found, and is extremely
789-
// unlikely to exist in the code prior to inlining, using this to
790-
// communicate between this method and `processInlinedCallBlocks`.
791-
// TODO: Fix this by refactoring the inliner interface.
792-
auto copyOp = builder.create<LLVM::SSACopyOp>(call->getLoc(), argument);
782+
783+
// This code is essentially a workaround for deficiencies in the inliner
784+
// interface: We need to transform operations *after* inlined based on the
785+
// argument attributes of the parameters *before* inlining. This method runs
786+
// prior to actual inlining and thus cannot transform the post-inlining
787+
// code, while `processInlinedCallBlocks` does not have access to
788+
// pre-inlining function arguments. Additionally, it is required to
789+
// distinguish which parameter an SSA value originally came from. As a
790+
// workaround until this is changed: Create an ssa.copy intrinsic with the
791+
// noalias attribute (when it was present before) that can easily be found,
792+
// and is extremely unlikely to exist in the code prior to inlining, using
793+
// this to communicate between this method and `processInlinedCallBlocks`.
794+
// TODO: Fix this by refactoring the inliner interface.
795+
auto copyOp = builder.create<LLVM::SSACopyOp>(call->getLoc(), argument);
796+
if (argumentAttrs.contains(LLVM::LLVMDialect::getNoAliasAttrName()))
793797
copyOp->setDiscardableAttr(
794798
builder.getStringAttr(LLVM::LLVMDialect::getNoAliasAttrName()),
795799
builder.getUnitAttr());
796-
return copyOp;
797-
}
798-
return argument;
800+
return copyOp;
799801
}
800802

801803
void processInlinedCallBlocks(

mlir/test/Dialect/LLVMIR/inlining-alias-scopes.mlir

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,29 @@ llvm.func @bar(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
234234

235235
// -----
236236

237+
// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
238+
// CHECK-DAG: #[[$ARG_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
239+
240+
llvm.func @foo(%arg0: !llvm.ptr {llvm.noalias}, %arg1: !llvm.ptr) {
241+
%0 = llvm.mlir.constant(5 : i64) : i64
242+
%1 = llvm.load %arg0 {alignment = 4 : i64} : !llvm.ptr -> f32
243+
%2 = llvm.getelementptr inbounds %arg1[%0] : (!llvm.ptr, i64) -> !llvm.ptr, f32
244+
llvm.store %1, %2 {alignment = 4 : i64} : f32, !llvm.ptr
245+
llvm.return
246+
}
247+
248+
// CHECK-LABEL: llvm.func @missing_noalias_on_one_ptr
249+
// CHECK: llvm.load
250+
// CHECK-SAME: alias_scopes = [#[[$ARG_SCOPE]]]
251+
// CHECK: llvm.store
252+
// CHECK-SAME: noalias_scopes = [#[[$ARG_SCOPE]]]
253+
llvm.func @missing_noalias_on_one_ptr(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: !llvm.ptr) {
254+
llvm.call @foo(%arg0, %arg2) : (!llvm.ptr, !llvm.ptr) -> ()
255+
llvm.return
256+
}
257+
258+
// -----
259+
237260
// CHECK-DAG: #[[DOMAIN:.*]] = #llvm.alias_scope_domain<{{.*}}>
238261
// CHECK-DAG: #[[$ARG0_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>
239262
// CHECK-DAG: #[[$ARG1_SCOPE:.*]] = #llvm.alias_scope<id = {{.*}}, domain = #[[DOMAIN]]{{(,.*)?}}>

0 commit comments

Comments
 (0)