• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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