Skip to content

Commit 7ff3a9a

Browse files
authored
[IR] Initial introduction of llvm.experimental.memset_pattern (#97583)
Supersedes the draft PR #94992, taking a different approach following feedback: * Lower in PreISelIntrinsicLowering * Don't require that the number of bytes to set is a compile-time constant * Define llvm.memset_pattern rather than llvm.memset_pattern.inline As discussed in the [RFC thread](https://discourse.llvm.org/t/rfc-introducing-an-llvm-memset-pattern-inline-intrinsic/79496), the intent is that the intrinsic will be lowered to loops, a sequence of stores, or libcalls depending on the expected cost and availability of libcalls on the target. Right now, there's just a single lowering path that aims to handle all cases. My intent would be to follow up with additional PRs that add additional optimisations when possible (e.g. when libcalls are available, when arguments are known to be constant etc).
1 parent 35710ab commit 7ff3a9a

File tree

15 files changed

+600
-1
lines changed

15 files changed

+600
-1
lines changed

llvm/docs/LangRef.rst

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15430,6 +15430,63 @@ The behavior of '``llvm.memset.inline.*``' is equivalent to the behavior of
1543015430
'``llvm.memset.*``', but the generated code is guaranteed not to call any
1543115431
external functions.
1543215432

15433+
.. _int_experimental_memset_pattern:
15434+
15435+
'``llvm.experimental.memset.pattern``' Intrinsic
15436+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15437+
15438+
Syntax:
15439+
"""""""
15440+
15441+
This is an overloaded intrinsic. You can use
15442+
``llvm.experimental.memset.pattern`` on any integer bit width and for
15443+
different address spaces. Not all targets support all bit widths however.
15444+
15445+
::
15446+
15447+
declare void @llvm.experimental.memset.pattern.p0.i128.i64(ptr <dest>, i128 <val>,
15448+
i64 <count>, i1 <isvolatile>)
15449+
15450+
Overview:
15451+
"""""""""
15452+
15453+
The '``llvm.experimental.memset.pattern.*``' intrinsics fill a block of memory
15454+
with a particular value. This may be expanded to an inline loop, a sequence of
15455+
stores, or a libcall depending on what is available for the target and the
15456+
expected performance and code size impact.
15457+
15458+
Arguments:
15459+
""""""""""
15460+
15461+
The first argument is a pointer to the destination to fill, the second
15462+
is the value with which to fill it, the third argument is an integer
15463+
argument specifying the number of times to fill the value, and the fourth is a
15464+
boolean indicating a volatile access.
15465+
15466+
The :ref:`align <attr_align>` parameter attribute can be provided
15467+
for the first argument.
15468+
15469+
If the ``isvolatile`` parameter is ``true``, the
15470+
``llvm.experimental.memset.pattern`` call is a :ref:`volatile operation
15471+
<volatile>`. The detailed access behavior is not very cleanly specified and it
15472+
is unwise to depend on it.
15473+
15474+
Semantics:
15475+
""""""""""
15476+
15477+
The '``llvm.experimental.memset.pattern*``' intrinsic fills memory starting at
15478+
the destination location with the given pattern ``<count>`` times,
15479+
incrementing by the allocation size of the type each time. The stores follow
15480+
the usual semantics of store instructions, including regarding endianness and
15481+
padding. If the argument is known to be aligned to some boundary, this can be
15482+
specified as an attribute on the argument.
15483+
15484+
If ``<count>`` is 0, it is no-op modulo the behavior of attributes attached to
15485+
the arguments.
15486+
If ``<count>`` is not a well-defined value, the behavior is undefined.
15487+
If ``<count>`` is not zero, ``<dest>`` should be well-defined, otherwise the
15488+
behavior is undefined.
15489+
1543315490
.. _int_sqrt:
1543415491

1543515492
'``llvm.sqrt.*``' Intrinsic

llvm/include/llvm/IR/InstVisitor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ class InstVisitor {
208208
RetTy visitDbgInfoIntrinsic(DbgInfoIntrinsic &I){ DELEGATE(IntrinsicInst); }
209209
RetTy visitMemSetInst(MemSetInst &I) { DELEGATE(MemIntrinsic); }
210210
RetTy visitMemSetInlineInst(MemSetInlineInst &I){ DELEGATE(MemSetInst); }
211+
RetTy visitMemSetPatternInst(MemSetPatternInst &I) {
212+
DELEGATE(IntrinsicInst);
213+
}
211214
RetTy visitMemCpyInst(MemCpyInst &I) { DELEGATE(MemTransferInst); }
212215
RetTy visitMemCpyInlineInst(MemCpyInlineInst &I){ DELEGATE(MemCpyInst); }
213216
RetTy visitMemMoveInst(MemMoveInst &I) { DELEGATE(MemTransferInst); }
@@ -295,6 +298,8 @@ class InstVisitor {
295298
case Intrinsic::memset: DELEGATE(MemSetInst);
296299
case Intrinsic::memset_inline:
297300
DELEGATE(MemSetInlineInst);
301+
case Intrinsic::experimental_memset_pattern:
302+
DELEGATE(MemSetPatternInst);
298303
case Intrinsic::vastart: DELEGATE(VAStartInst);
299304
case Intrinsic::vaend: DELEGATE(VAEndInst);
300305
case Intrinsic::vacopy: DELEGATE(VACopyInst);

llvm/include/llvm/IR/IntrinsicInst.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1263,6 +1263,41 @@ class MemSetInlineInst : public MemSetInst {
12631263
}
12641264
};
12651265

1266+
/// This is the base class for llvm.experimental.memset.pattern
1267+
class MemSetPatternIntrinsic : public MemIntrinsicBase<MemIntrinsic> {
1268+
private:
1269+
enum { ARG_VOLATILE = 3 };
1270+
1271+
public:
1272+
ConstantInt *getVolatileCst() const {
1273+
return cast<ConstantInt>(const_cast<Value *>(getArgOperand(ARG_VOLATILE)));
1274+
}
1275+
1276+
bool isVolatile() const { return !getVolatileCst()->isZero(); }
1277+
1278+
void setVolatile(Constant *V) { setArgOperand(ARG_VOLATILE, V); }
1279+
1280+
// Methods for support of type inquiry through isa, cast, and dyn_cast:
1281+
static bool classof(const IntrinsicInst *I) {
1282+
return I->getIntrinsicID() == Intrinsic::experimental_memset_pattern;
1283+
}
1284+
static bool classof(const Value *V) {
1285+
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
1286+
}
1287+
};
1288+
1289+
/// This class wraps the llvm.experimental.memset.pattern intrinsic.
1290+
class MemSetPatternInst : public MemSetBase<MemSetPatternIntrinsic> {
1291+
public:
1292+
// Methods for support type inquiry through isa, cast, and dyn_cast:
1293+
static bool classof(const IntrinsicInst *I) {
1294+
return I->getIntrinsicID() == Intrinsic::experimental_memset_pattern;
1295+
}
1296+
static bool classof(const Value *V) {
1297+
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
1298+
}
1299+
};
1300+
12661301
/// This class wraps the llvm.memcpy/memmove intrinsics.
12671302
class MemTransferInst : public MemTransferBase<MemIntrinsic> {
12681303
public:

llvm/include/llvm/IR/Intrinsics.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,6 +1006,17 @@ def int_memset_inline
10061006
NoCapture<ArgIndex<0>>, WriteOnly<ArgIndex<0>>,
10071007
ImmArg<ArgIndex<3>>]>;
10081008

1009+
// Memset variant that writes a given pattern.
1010+
def int_experimental_memset_pattern
1011+
: Intrinsic<[],
1012+
[llvm_anyptr_ty, // Destination.
1013+
llvm_anyint_ty, // Pattern value.
1014+
llvm_anyint_ty, // Count (number of times to fill value).
1015+
llvm_i1_ty], // IsVolatile.
1016+
[IntrWriteMem, IntrArgMemOnly, IntrWillReturn, IntrNoFree, IntrNoCallback,
1017+
NoCapture<ArgIndex<0>>, WriteOnly<ArgIndex<0>>,
1018+
ImmArg<ArgIndex<3>>]>;
1019+
10091020
// FIXME: Add version of these floating point intrinsics which allow non-default
10101021
// rounding modes and FP exception handling.
10111022

llvm/include/llvm/Transforms/Utils/LowerMemIntrinsics.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Instruction;
2525
class MemCpyInst;
2626
class MemMoveInst;
2727
class MemSetInst;
28+
class MemSetPatternInst;
2829
class ScalarEvolution;
2930
class TargetTransformInfo;
3031
class Value;
@@ -57,6 +58,9 @@ bool expandMemMoveAsLoop(MemMoveInst *MemMove, const TargetTransformInfo &TTI);
5758
/// Expand \p MemSet as a loop. \p MemSet is not deleted.
5859
void expandMemSetAsLoop(MemSetInst *MemSet);
5960

61+
/// Expand \p MemSetPattern as a loop. \p MemSet is not deleted.
62+
void expandMemSetPatternAsLoop(MemSetPatternInst *MemSet);
63+
6064
/// Expand \p AtomicMemCpy as a loop. \p AtomicMemCpy is not deleted.
6165
void expandAtomicMemCpyAsLoop(AtomicMemCpyInst *AtomicMemCpy,
6266
const TargetTransformInfo &TTI,

llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,13 @@ bool PreISelIntrinsicLowering::expandMemIntrinsicUses(Function &F) const {
320320
Memset->eraseFromParent();
321321
break;
322322
}
323+
case Intrinsic::experimental_memset_pattern: {
324+
auto *Memset = cast<MemSetPatternInst>(Inst);
325+
expandMemSetPatternAsLoop(Memset);
326+
Changed = true;
327+
Memset->eraseFromParent();
328+
break;
329+
}
323330
default:
324331
llvm_unreachable("unhandled intrinsic");
325332
}
@@ -339,6 +346,7 @@ bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const {
339346
case Intrinsic::memmove:
340347
case Intrinsic::memset:
341348
case Intrinsic::memset_inline:
349+
case Intrinsic::experimental_memset_pattern:
342350
Changed |= expandMemIntrinsicUses(F);
343351
break;
344352
case Intrinsic::load_relative:

llvm/lib/IR/Verifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5519,7 +5519,8 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
55195519
case Intrinsic::memcpy_inline:
55205520
case Intrinsic::memmove:
55215521
case Intrinsic::memset:
5522-
case Intrinsic::memset_inline: {
5522+
case Intrinsic::memset_inline:
5523+
case Intrinsic::experimental_memset_pattern: {
55235524
break;
55245525
}
55255526
case Intrinsic::memcpy_element_unordered_atomic:

llvm/lib/Transforms/Utils/LowerMemIntrinsics.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,15 @@ void llvm::expandMemSetAsLoop(MemSetInst *Memset) {
970970
Memset->isVolatile());
971971
}
972972

973+
void llvm::expandMemSetPatternAsLoop(MemSetPatternInst *Memset) {
974+
createMemSetLoop(/* InsertBefore=*/Memset,
975+
/* DstAddr=*/Memset->getRawDest(),
976+
/* CopyLen=*/Memset->getLength(),
977+
/* SetValue=*/Memset->getValue(),
978+
/* Alignment=*/Memset->getDestAlign().valueOrOne(),
979+
Memset->isVolatile());
980+
}
981+
973982
void llvm::expandAtomicMemCpyAsLoop(AtomicMemCpyInst *AtomicMemcpy,
974983
const TargetTransformInfo &TTI,
975984
ScalarEvolution *SE) {

0 commit comments

Comments
 (0)