1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "aarch64_memlayout.h"
17 #include "aarch64_cgfunc.h"
18
19 namespace maplebe {
20 using namespace maple;
21
22 /*
23 * Returns stack space required for a call
24 * which is used to pass arguments that cannot be
25 * passed through registers
26 */
ComputeStackSpaceRequirementForCall(StmtNode & stmt,int32 & aggCopySize,bool isIcall)27 uint32 AArch64MemLayout::ComputeStackSpaceRequirementForCall(StmtNode &stmt, int32 &aggCopySize, bool isIcall)
28 {
29 /* instantiate a parm locator */
30 CHECK_FATAL(cgFunc != nullptr, "nullptr check");
31 CCImpl &parmLocator = *static_cast<AArch64CGFunc *>(cgFunc)->GetOrCreateLocator(CCImpl::GetCallConvKind(stmt));
32 uint32 sizeOfArgsToStkPass = 0;
33 uint32 i = 0;
34 /* An indirect call's first operand is the invocation target */
35 if (isIcall) {
36 ++i;
37 }
38
39 if (stmt.GetOpCode() == OP_call) {
40 CallNode *callNode = static_cast<CallNode *>(&stmt);
41 MIRFunction *fn = GlobalTables::GetFunctionTable().GetFunctionFromPuidx(callNode->GetPUIdx());
42 CHECK_FATAL(fn != nullptr, "get MIRFunction failed");
43 CHECK_NULL_FATAL(be.GetMIRModule().CurFunction());
44 }
45
46 aggCopySize = 0;
47 for (uint32 anum = 0; i < stmt.NumOpnds(); ++i, ++anum) {
48 BaseNode *opnd = stmt.Opnd(i);
49 MIRType *ty = nullptr;
50 ty = GlobalTables::GetTypeTable().GetTypeTable()[static_cast<uint32>(opnd->GetPrimType())];
51 CCLocInfo ploc;
52 aggCopySize += static_cast<int32>(parmLocator.LocateNextParm(*ty, ploc));
53 if (ploc.reg0 != 0) {
54 continue; /* passed in register, so no effect on actual area */
55 }
56 sizeOfArgsToStkPass = static_cast<uint32>(
57 RoundUp(static_cast<uint32>(ploc.memOffset + ploc.memSize), static_cast<uint64>(GetPointerSize())));
58 }
59 return sizeOfArgsToStkPass;
60 }
61
SetSizeAlignForTypeIdx(uint32 typeIdx,uint32 & size,uint32 & align) const62 void AArch64MemLayout::SetSizeAlignForTypeIdx(uint32 typeIdx, uint32 &size, uint32 &align) const
63 {
64 auto *mirType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(typeIdx);
65
66 align = mirType->GetAlign();
67 size = static_cast<uint32>(mirType->GetSize());
68 }
69
LayoutFormalParams()70 void AArch64MemLayout::LayoutFormalParams()
71 {
72 CCImpl &parmLocator = *static_cast<AArch64CGFunc *>(cgFunc)->GetOrCreateLocator(cgFunc->GetCurCallConvKind());
73 CCLocInfo ploc;
74 for (size_t i = 0; i < mirFunction->GetFormalCount(); ++i) {
75 MIRSymbol *sym = mirFunction->GetFormal(i);
76 uint32 stIndex = sym->GetStIndex();
77 AArch64SymbolAlloc *symLoc = memAllocator->GetMemPool()->New<AArch64SymbolAlloc>();
78 SetSymAllocInfo(stIndex, *symLoc);
79 MIRType *ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(mirFunction->GetFormalDefVec()[i].formalTyIdx);
80 CHECK_FATAL(ty != nullptr, "nullptr check");
81 uint32 ptyIdx = ty->GetTypeIndex();
82 parmLocator.LocateNextParm(*ty, ploc, i == 0, mirFunction->GetMIRFuncType());
83 if (ploc.reg0 != kRinvalid) { /* register */
84 symLoc->SetRegisters(static_cast<AArch64reg>(ploc.reg0), static_cast<AArch64reg>(ploc.reg1),
85 static_cast<AArch64reg>(ploc.reg2), static_cast<AArch64reg>(ploc.reg3));
86 if (!sym->IsPreg()) {
87 uint32 size;
88 uint32 align;
89 SetSizeAlignForTypeIdx(ptyIdx, size, align);
90 symLoc->SetMemSegment(GetSegArgsRegPassed());
91 /* the type's alignment requirement may be smaller than a registser's byte size */
92 segArgsRegPassed.SetSize(static_cast<uint32>(RoundUp(segArgsRegPassed.GetSize(), align)));
93 symLoc->SetOffset(segArgsRegPassed.GetSize());
94 segArgsRegPassed.SetSize(segArgsRegPassed.GetSize() + size);
95 }
96 } else { /* stack */
97 uint32 size;
98 uint32 align;
99 SetSizeAlignForTypeIdx(ptyIdx, size, align);
100 symLoc->SetMemSegment(GetSegArgsStkPassed());
101 segArgsStkPassed.SetSize(static_cast<uint32>(RoundUp(segArgsStkPassed.GetSize(), align)));
102 symLoc->SetOffset(segArgsStkPassed.GetSize());
103 segArgsStkPassed.SetSize(segArgsStkPassed.GetSize() + size);
104 /* We need it as dictated by the AArch64 ABI $5.4.2 C12 */
105 segArgsStkPassed.SetSize(static_cast<uint32>(RoundUp(segArgsStkPassed.GetSize(), GetPointerSize())));
106 }
107 }
108 }
109
110 // stack frame layout V2.1
111 // stack frame -> layout out some local variable on cold zone
112 // 1. layout small variables near sp
113 // 2. layout cold variables in cold area
114 // ||----------------------------|
115 // | args passed on the stack |
116 // ||----------------------------|
117 // | GR saved area | 16 byte align|
118 // ||---------------------------|
119 // | VR saved area | 16 byte align |
120 // ||---------------------------- |
121 // | stack protect area | total 16 byte|
122 // ||----------------------------|
123 // | cold area | 16 byte align |
124 // ||----------------------------| <- unadjustvary base
125 // | callee saved |
126 // ||----------------------------|
127 // | spill |
128 // ||----------------------------|
129 // | reg saved |
130 // ||----------------------------|
131 // | local variables |
132 // ||----------------------------|
133 // | PREV_FP, PREV_LR |
134 // ||----------------------------|<- Frame Pointer
135 // | variable-sized local vars |
136 // | (VLAs) |
137 // ||----------------------------|
138 // | args to pass through stack |
139 // ||----------------------------|
140
LayoutLocalVariables(std::vector<MIRSymbol * > & tempVar,std::vector<MIRSymbol * > & returnDelays)141 void AArch64MemLayout::LayoutLocalVariables(std::vector<MIRSymbol *> &tempVar, std::vector<MIRSymbol *> &returnDelays)
142 {
143 uint32 symTabSize = mirFunction->GetSymTab()->GetSymbolTableSize();
144 for (uint32 i = 0; i < symTabSize; ++i) {
145 MIRSymbol *sym = mirFunction->GetSymTab()->GetSymbolFromStIdx(i);
146 if (sym == nullptr || sym->GetStorageClass() != kScAuto || sym->IsDeleted()) {
147 continue;
148 }
149 uint32 stIndex = sym->GetStIndex();
150 TyIdx tyIdx = sym->GetTyIdx();
151 auto *symLoc = memAllocator->GetMemPool()->New<AArch64SymbolAlloc>();
152 SetSymAllocInfo(stIndex, *symLoc);
153 CHECK_FATAL(!symLoc->IsRegister(), "expect not register");
154 symLoc->SetMemSegment(segLocals);
155 MIRType *ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(tyIdx);
156 uint32 align = ty->GetAlign();
157 segLocals.SetSize(static_cast<uint32>(RoundUp(segLocals.GetSize(), align)));
158 symLoc->SetOffset(segLocals.GetSize());
159 segLocals.SetSize(segLocals.GetSize() +
160 static_cast<uint32>(GlobalTables::GetTypeTable().GetTypeFromTyIdx(tyIdx)->GetSize()));
161 }
162 }
163
LayoutReturnRef(std::vector<MIRSymbol * > & returnDelays,int32 & structCopySize,int32 & maxParmStackSize)164 void AArch64MemLayout::LayoutReturnRef(std::vector<MIRSymbol *> &returnDelays, int32 &structCopySize,
165 int32 &maxParmStackSize)
166 {
167 segArgsToStkPass.SetSize(FindLargestActualArea(structCopySize));
168 maxParmStackSize = static_cast<int32>(segArgsToStkPass.GetSize());
169 segRefLocals.SetSize(static_cast<uint32>(RoundUp(segRefLocals.GetSize(), GetPointerSize())));
170 segLocals.SetSize(static_cast<uint32>(RoundUp(segLocals.GetSize(), GetPointerSize())));
171 }
172
LayoutActualParams()173 void AArch64MemLayout::LayoutActualParams()
174 {
175 for (size_t i = 0; i < mirFunction->GetFormalCount(); ++i) {
176 MIRSymbol *sym = mirFunction->GetFormal(i);
177 if (sym->IsPreg()) {
178 continue;
179 }
180 uint32 stIndex = sym->GetStIndex();
181 AArch64SymbolAlloc *symLoc = static_cast<AArch64SymbolAlloc *>(GetSymAllocInfo(stIndex));
182 if (symLoc->GetMemSegment() == &GetSegArgsRegPassed()) { /* register */
183 /*
184 * In O0, we store parameters passed via registers into memory.
185 * So, each of such parameter needs to get assigned storage in stack.
186 * If a function parameter is never accessed in the function body,
187 * and if we don't create its memory operand here, its offset gets
188 * computed when the instruction to store its value into stack
189 * is generated in the prologue when its memory operand is created.
190 * But, the parameter would see a different StackFrameSize than
191 * the parameters that are accessed in the body, because
192 * the size of the storage for FP/LR is added to the stack frame
193 * size in between.
194 * To make offset assignment easier, we create a memory operand
195 * for each of function parameters in advance.
196 * This has to be done after all of formal parameters and local
197 * variables get assigned their respecitve storage, i.e.
198 * CallFrameSize (discounting callee-saved and FP/LR) is known.
199 */
200 MIRType *ty = GlobalTables::GetTypeTable().GetTypeFromTyIdx(mirFunction->GetFormalDefVec()[i].formalTyIdx);
201 uint32 ptyIdx = ty->GetTypeIndex();
202 static_cast<AArch64CGFunc *>(cgFunc)->GetOrCreateMemOpnd(
203 *sym, 0, GlobalTables::GetTypeTable().GetTypeFromTyIdx(ptyIdx)->GetAlign() * kBitsPerByte);
204 }
205 }
206 }
207
LayoutStackFrame(int32 & structCopySize,int32 & maxParmStackSize)208 void AArch64MemLayout::LayoutStackFrame(int32 &structCopySize, int32 &maxParmStackSize)
209 {
210 LayoutFormalParams();
211 /*
212 * We do need this as LDR/STR with immediate
213 * requires imm be aligned at a 8/4-byte boundary,
214 * and local varirables may need 8-byte alignment.
215 */
216 if (CGOptions::IsArm64ilp32()) {
217 segArgsRegPassed.SetSize(RoundUp(segArgsRegPassed.GetSize(), k8ByteSize));
218 /* we do need this as SP has to be aligned at a 16-bytes bounardy */
219 segArgsStkPassed.SetSize(RoundUp(segArgsStkPassed.GetSize(), k8ByteSize + k8ByteSize));
220 } else {
221 segArgsRegPassed.SetSize(static_cast<uint32>(RoundUp(segArgsRegPassed.GetSize(), GetPointerSize())));
222 segArgsStkPassed.SetSize(
223 static_cast<uint32>(RoundUp(segArgsStkPassed.GetSize(), GetPointerSize() + GetPointerSize())));
224 }
225 /* allocate the local variables in the stack */
226 std::vector<MIRSymbol *> eaTempVar;
227 std::vector<MIRSymbol *> retDelays;
228 LayoutLocalVariables(eaTempVar, retDelays);
229
230 /* handle ret_ref sym now */
231 LayoutReturnRef(retDelays, structCopySize, maxParmStackSize);
232
233 /*
234 * for the actual arguments that cannot be pass through registers
235 * need to allocate space for caller-save registers
236 */
237 LayoutActualParams();
238
239 fixStackSize = static_cast<int32>(RealStackFrameSize());
240 cgFunc->SetUseFP(cgFunc->UseFP() || fixStackSize > kMaxPimm32);
241 }
242 // from cold area to bottom of stk
243 // [cold,16] + [GR, 16] + [VR, 16] + stack protect (if has)
GetSizeOfColdToStk() const244 uint64 AArch64MemLayout::GetSizeOfColdToStk() const
245 {
246 uint64 total = 0;
247 auto coldsize = RoundUp(GetSizeOfSegCold(), k16BitSize);
248 total += coldsize;
249 return total;
250 }
251
IsSegMentVaried(const MemSegment * seg) const252 bool AArch64MemLayout::IsSegMentVaried(const MemSegment *seg) const
253 {
254 if (seg->GetMemSegmentKind() == kMsArgsStkPassed || seg->GetMemSegmentKind() == kMsCold) {
255 return true;
256 }
257 return false;
258 }
259
StackFrameSize() const260 uint64 AArch64MemLayout::StackFrameSize() const
261 {
262 // regpassed + calleesaved + reflocals + locals + spill + cold + args to callee
263 uint64 total = segArgsRegPassed.GetSize() + static_cast<AArch64CGFunc *>(cgFunc)->SizeOfCalleeSaved() +
264 GetSizeOfRefLocals() + Locals().GetSize() + GetSizeOfSpillReg() +
265 cgFunc->GetFunction().GetFrameReseverdSlot();
266
267 auto coldsize = RoundUp(GetSizeOfSegCold(), k16BitSize);
268 total += coldsize;
269 total += segArgsToStkPass.GetSize();
270 return RoundUp(total, kAarch64StackPtrAlignment);
271 }
272
273 // [regpass] + [callee save] + [reflocal] + [local] + [spill] + [cold,16] + [GR,16] + [VR,16] + stack protect (if has)
RealStackFrameSize() const274 uint32 AArch64MemLayout::RealStackFrameSize() const
275 {
276 auto size = StackFrameSize();
277 return static_cast<uint32>(size);
278 }
279 // fp - callee base =
280 // RealStackFrameSize - [GR,16] - [VR,16] - [cold,16] - ([callee] - 16(fplr)) - stack protect - stkpass
281 // callsave area size includes fp lr, real callee save area size is callee save size - fplr
282 // fp lr located on top of args pass area.
GetCalleeSaveBaseLoc() const283 int32 AArch64MemLayout::GetCalleeSaveBaseLoc() const
284 {
285 DEBUG_ASSERT(cgFunc != nullptr, "cgfunc shound not be nullptr");
286 uint32 offset = RealStackFrameSize() - static_cast<AArch64CGFunc *>(cgFunc)->SizeOfCalleeSaved();
287 offset = (offset - SizeOfArgsToStackPass()) + kAarch64SizeOfFplr - cgFunc->GetFunction().GetFrameReseverdSlot();
288 offset -= static_cast<uint32>(RoundUp(GetSizeOfSegCold(), k16BitSize));
289 return static_cast<int32>(offset);
290 }
291 } /* namespace maplebe */
292