1 //===-- AArch64SelectionDAGInfo.cpp - AArch64 SelectionDAG Info -----------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the AArch64SelectionDAGInfo class.
11 //===----------------------------------------------------------------------===//
13 #include "AArch64TargetMachine.h"
16 #define DEBUG_TYPE "aarch64-selectiondag-info"
19 LowerToSMERoutines("aarch64-lower-to-sme-routines", cl::Hidden
,
20 cl::desc("Enable AArch64 SME memory operations "
21 "to lower to librt functions"),
24 SDValue
AArch64SelectionDAGInfo::EmitMOPS(AArch64ISD::NodeType SDOpcode
,
25 SelectionDAG
&DAG
, const SDLoc
&DL
,
26 SDValue Chain
, SDValue Dst
,
27 SDValue SrcOrValue
, SDValue Size
,
28 Align Alignment
, bool isVolatile
,
29 MachinePointerInfo DstPtrInfo
,
30 MachinePointerInfo SrcPtrInfo
) const {
32 // Get the constant size of the copy/set.
33 uint64_t ConstSize
= 0;
34 if (auto *C
= dyn_cast
<ConstantSDNode
>(Size
))
35 ConstSize
= C
->getZExtValue();
37 const bool IsSet
= SDOpcode
== AArch64ISD::MOPS_MEMSET
||
38 SDOpcode
== AArch64ISD::MOPS_MEMSET_TAGGING
;
40 const auto MachineOpcode
= [&]() {
42 case AArch64ISD::MOPS_MEMSET
:
43 return AArch64::MOPSMemorySetPseudo
;
44 case AArch64ISD::MOPS_MEMSET_TAGGING
:
45 return AArch64::MOPSMemorySetTaggingPseudo
;
46 case AArch64ISD::MOPS_MEMCOPY
:
47 return AArch64::MOPSMemoryCopyPseudo
;
48 case AArch64ISD::MOPS_MEMMOVE
:
49 return AArch64::MOPSMemoryMovePseudo
;
51 llvm_unreachable("Unhandled MOPS ISD Opcode");
55 MachineFunction
&MF
= DAG
.getMachineFunction();
58 isVolatile
? MachineMemOperand::MOVolatile
: MachineMemOperand::MONone
;
59 auto DstFlags
= MachineMemOperand::MOStore
| Vol
;
61 MF
.getMachineMemOperand(DstPtrInfo
, DstFlags
, ConstSize
, Alignment
);
64 // Extend value to i64, if required.
65 if (SrcOrValue
.getValueType() != MVT::i64
)
66 SrcOrValue
= DAG
.getNode(ISD::ANY_EXTEND
, DL
, MVT::i64
, SrcOrValue
);
67 SDValue Ops
[] = {Dst
, Size
, SrcOrValue
, Chain
};
68 const EVT ResultTys
[] = {MVT::i64
, MVT::i64
, MVT::Other
};
69 MachineSDNode
*Node
= DAG
.getMachineNode(MachineOpcode
, DL
, ResultTys
, Ops
);
70 DAG
.setNodeMemRefs(Node
, {DstOp
});
71 return SDValue(Node
, 2);
73 SDValue Ops
[] = {Dst
, SrcOrValue
, Size
, Chain
};
74 const EVT ResultTys
[] = {MVT::i64
, MVT::i64
, MVT::i64
, MVT::Other
};
75 MachineSDNode
*Node
= DAG
.getMachineNode(MachineOpcode
, DL
, ResultTys
, Ops
);
77 auto SrcFlags
= MachineMemOperand::MOLoad
| Vol
;
79 MF
.getMachineMemOperand(SrcPtrInfo
, SrcFlags
, ConstSize
, Alignment
);
80 DAG
.setNodeMemRefs(Node
, {DstOp
, SrcOp
});
81 return SDValue(Node
, 3);
85 SDValue
AArch64SelectionDAGInfo::EmitStreamingCompatibleMemLibCall(
86 SelectionDAG
&DAG
, const SDLoc
&DL
, SDValue Chain
, SDValue Dst
, SDValue Src
,
87 SDValue Size
, RTLIB::Libcall LC
) const {
88 const AArch64Subtarget
&STI
=
89 DAG
.getMachineFunction().getSubtarget
<AArch64Subtarget
>();
90 const AArch64TargetLowering
*TLI
= STI
.getTargetLowering();
92 TargetLowering::ArgListEntry DstEntry
;
93 DstEntry
.Ty
= PointerType::getUnqual(*DAG
.getContext());
95 TargetLowering::ArgListTy Args
;
96 Args
.push_back(DstEntry
);
97 EVT PointerVT
= TLI
->getPointerTy(DAG
.getDataLayout());
100 case RTLIB::MEMCPY
: {
101 TargetLowering::ArgListEntry Entry
;
102 Entry
.Ty
= PointerType::getUnqual(*DAG
.getContext());
103 Symbol
= DAG
.getExternalSymbol("__arm_sc_memcpy", PointerVT
);
105 Args
.push_back(Entry
);
108 case RTLIB::MEMMOVE
: {
109 TargetLowering::ArgListEntry Entry
;
110 Entry
.Ty
= PointerType::getUnqual(*DAG
.getContext());
111 Symbol
= DAG
.getExternalSymbol("__arm_sc_memmove", PointerVT
);
113 Args
.push_back(Entry
);
116 case RTLIB::MEMSET
: {
117 TargetLowering::ArgListEntry Entry
;
118 Entry
.Ty
= Type::getInt32Ty(*DAG
.getContext());
119 Symbol
= DAG
.getExternalSymbol("__arm_sc_memset", PointerVT
);
120 Src
= DAG
.getZExtOrTrunc(Src
, DL
, MVT::i32
);
122 Args
.push_back(Entry
);
129 TargetLowering::ArgListEntry SizeEntry
;
130 SizeEntry
.Node
= Size
;
131 SizeEntry
.Ty
= DAG
.getDataLayout().getIntPtrType(*DAG
.getContext());
132 Args
.push_back(SizeEntry
);
133 assert(Symbol
->getOpcode() == ISD::ExternalSymbol
&&
134 "Function name is not set");
136 TargetLowering::CallLoweringInfo
CLI(DAG
);
137 PointerType
*RetTy
= PointerType::getUnqual(*DAG
.getContext());
138 CLI
.setDebugLoc(DL
).setChain(Chain
).setLibCallee(
139 TLI
->getLibcallCallingConv(LC
), RetTy
, Symbol
, std::move(Args
));
140 return TLI
->LowerCallTo(CLI
).second
;
143 SDValue
AArch64SelectionDAGInfo::EmitTargetCodeForMemcpy(
144 SelectionDAG
&DAG
, const SDLoc
&DL
, SDValue Chain
, SDValue Dst
, SDValue Src
,
145 SDValue Size
, Align Alignment
, bool isVolatile
, bool AlwaysInline
,
146 MachinePointerInfo DstPtrInfo
, MachinePointerInfo SrcPtrInfo
) const {
147 const AArch64Subtarget
&STI
=
148 DAG
.getMachineFunction().getSubtarget
<AArch64Subtarget
>();
151 return EmitMOPS(AArch64ISD::MOPS_MEMCOPY
, DAG
, DL
, Chain
, Dst
, Src
, Size
,
152 Alignment
, isVolatile
, DstPtrInfo
, SrcPtrInfo
);
154 SMEAttrs
Attrs(DAG
.getMachineFunction().getFunction());
155 if (LowerToSMERoutines
&& !Attrs
.hasNonStreamingInterfaceAndBody())
156 return EmitStreamingCompatibleMemLibCall(DAG
, DL
, Chain
, Dst
, Src
, Size
,
161 SDValue
AArch64SelectionDAGInfo::EmitTargetCodeForMemset(
162 SelectionDAG
&DAG
, const SDLoc
&dl
, SDValue Chain
, SDValue Dst
, SDValue Src
,
163 SDValue Size
, Align Alignment
, bool isVolatile
, bool AlwaysInline
,
164 MachinePointerInfo DstPtrInfo
) const {
165 const AArch64Subtarget
&STI
=
166 DAG
.getMachineFunction().getSubtarget
<AArch64Subtarget
>();
169 return EmitMOPS(AArch64ISD::MOPS_MEMSET
, DAG
, dl
, Chain
, Dst
, Src
, Size
,
170 Alignment
, isVolatile
, DstPtrInfo
, MachinePointerInfo
{});
172 SMEAttrs
Attrs(DAG
.getMachineFunction().getFunction());
173 if (LowerToSMERoutines
&& !Attrs
.hasNonStreamingInterfaceAndBody())
174 return EmitStreamingCompatibleMemLibCall(DAG
, dl
, Chain
, Dst
, Src
, Size
,
179 SDValue
AArch64SelectionDAGInfo::EmitTargetCodeForMemmove(
180 SelectionDAG
&DAG
, const SDLoc
&dl
, SDValue Chain
, SDValue Dst
, SDValue Src
,
181 SDValue Size
, Align Alignment
, bool isVolatile
,
182 MachinePointerInfo DstPtrInfo
, MachinePointerInfo SrcPtrInfo
) const {
183 const AArch64Subtarget
&STI
=
184 DAG
.getMachineFunction().getSubtarget
<AArch64Subtarget
>();
187 return EmitMOPS(AArch64ISD::MOPS_MEMMOVE
, DAG
, dl
, Chain
, Dst
, Src
, Size
,
188 Alignment
, isVolatile
, DstPtrInfo
, SrcPtrInfo
);
190 SMEAttrs
Attrs(DAG
.getMachineFunction().getFunction());
191 if (LowerToSMERoutines
&& !Attrs
.hasNonStreamingInterfaceAndBody())
192 return EmitStreamingCompatibleMemLibCall(DAG
, dl
, Chain
, Dst
, Src
, Size
,
197 static const int kSetTagLoopThreshold
= 176;
199 static SDValue
EmitUnrolledSetTag(SelectionDAG
&DAG
, const SDLoc
&dl
,
200 SDValue Chain
, SDValue Ptr
, uint64_t ObjSize
,
201 const MachineMemOperand
*BaseMemOperand
,
203 MachineFunction
&MF
= DAG
.getMachineFunction();
204 unsigned ObjSizeScaled
= ObjSize
/ 16;
206 SDValue TagSrc
= Ptr
;
207 if (Ptr
.getOpcode() == ISD::FrameIndex
) {
208 int FI
= cast
<FrameIndexSDNode
>(Ptr
)->getIndex();
209 Ptr
= DAG
.getTargetFrameIndex(FI
, MVT::i64
);
210 // A frame index operand may end up as [SP + offset] => it is fine to use SP
211 // register as the tag source.
212 TagSrc
= DAG
.getRegister(AArch64::SP
, MVT::i64
);
215 const unsigned OpCode1
= ZeroData
? AArch64ISD::STZG
: AArch64ISD::STG
;
216 const unsigned OpCode2
= ZeroData
? AArch64ISD::STZ2G
: AArch64ISD::ST2G
;
218 SmallVector
<SDValue
, 8> OutChains
;
219 unsigned OffsetScaled
= 0;
220 while (OffsetScaled
< ObjSizeScaled
) {
221 if (ObjSizeScaled
- OffsetScaled
>= 2) {
222 SDValue AddrNode
= DAG
.getMemBasePlusOffset(
223 Ptr
, TypeSize::getFixed(OffsetScaled
* 16), dl
);
224 SDValue St
= DAG
.getMemIntrinsicNode(
225 OpCode2
, dl
, DAG
.getVTList(MVT::Other
),
226 {Chain
, TagSrc
, AddrNode
},
228 MF
.getMachineMemOperand(BaseMemOperand
, OffsetScaled
* 16, 16 * 2));
230 OutChains
.push_back(St
);
234 if (ObjSizeScaled
- OffsetScaled
> 0) {
235 SDValue AddrNode
= DAG
.getMemBasePlusOffset(
236 Ptr
, TypeSize::getFixed(OffsetScaled
* 16), dl
);
237 SDValue St
= DAG
.getMemIntrinsicNode(
238 OpCode1
, dl
, DAG
.getVTList(MVT::Other
),
239 {Chain
, TagSrc
, AddrNode
},
241 MF
.getMachineMemOperand(BaseMemOperand
, OffsetScaled
* 16, 16));
243 OutChains
.push_back(St
);
247 SDValue Res
= DAG
.getNode(ISD::TokenFactor
, dl
, MVT::Other
, OutChains
);
251 SDValue
AArch64SelectionDAGInfo::EmitTargetCodeForSetTag(
252 SelectionDAG
&DAG
, const SDLoc
&dl
, SDValue Chain
, SDValue Addr
,
253 SDValue Size
, MachinePointerInfo DstPtrInfo
, bool ZeroData
) const {
254 uint64_t ObjSize
= Size
->getAsZExtVal();
255 assert(ObjSize
% 16 == 0);
257 MachineFunction
&MF
= DAG
.getMachineFunction();
258 MachineMemOperand
*BaseMemOperand
= MF
.getMachineMemOperand(
259 DstPtrInfo
, MachineMemOperand::MOStore
, ObjSize
, Align(16));
261 bool UseSetTagRangeLoop
=
262 kSetTagLoopThreshold
>= 0 && (int)ObjSize
>= kSetTagLoopThreshold
;
263 if (!UseSetTagRangeLoop
)
264 return EmitUnrolledSetTag(DAG
, dl
, Chain
, Addr
, ObjSize
, BaseMemOperand
,
267 const EVT ResTys
[] = {MVT::i64
, MVT::i64
, MVT::Other
};
270 if (Addr
.getOpcode() == ISD::FrameIndex
) {
271 int FI
= cast
<FrameIndexSDNode
>(Addr
)->getIndex();
272 Addr
= DAG
.getTargetFrameIndex(FI
, MVT::i64
);
273 Opcode
= ZeroData
? AArch64::STZGloop
: AArch64::STGloop
;
275 Opcode
= ZeroData
? AArch64::STZGloop_wback
: AArch64::STGloop_wback
;
277 SDValue Ops
[] = {DAG
.getTargetConstant(ObjSize
, dl
, MVT::i64
), Addr
, Chain
};
278 SDNode
*St
= DAG
.getMachineNode(Opcode
, dl
, ResTys
, Ops
);
280 DAG
.setNodeMemRefs(cast
<MachineSDNode
>(St
), {BaseMemOperand
});
281 return SDValue(St
, 2);