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 #ifndef MAPLEBE_INCLUDE_CG_MEMLAYOUT_H 17 #define MAPLEBE_INCLUDE_CG_MEMLAYOUT_H 18 19 /* C++ headers. */ 20 #include <cstddef> 21 #include <utility> 22 #include "becommon.h" 23 #include "mir_function.h" 24 #include "mir_nodes.h" /* StmtNode */ 25 26 namespace maplebe { 27 using regno_t = uint32; 28 enum MemSegmentKind : uint8 { 29 kMsUnknown, 30 /* 31 * Function arguments that are not passed through registers 32 * are passed to the callee through stack. 33 */ 34 kMsArgsStkPassed, 35 /* 36 * In between MS_args_stackpassed and kMsArgsRegpassed, 37 * we store call-saved registers if any. 38 */ 39 /* 40 * Args passed via registers according to the architecture-specific ABI 41 * may need be stored in stack. 42 * 1) In the unoptimized version, we implement a model (similar to GCC -O0) 43 * where all the values are initially stored in the memory and 44 * loaded into registers when needed, and stored back to the memory when 45 * their uses are done. 46 * 2) In an optimized version, some register-passed values may need to be 47 * spilled into memory. We allocate the space in this Memory segment. 48 * (or we may allocate them in caller-saved; may be this is better...) 49 */ 50 kMsArgsRegPassed, 51 /* 52 * GR/VR Save areas for unnamed arguments under vararg functions 53 */ 54 kMsGrSaveArea, 55 kMsVrSaveArea, 56 /* local (auto) variables */ 57 kMsRefLocals, 58 kMsLocals, 59 kMsSpillReg, 60 /* 61 * In between kMsLocals and MS_args_to_stackpass, we allocate 62 * a register-spill area and space for caller-saved registers 63 */ 64 /* 65 * When a function calls another which takes some arguments 66 * that cannot be passed through registers, it is the caller's 67 * responsibility to allocate space for those arguments in memory. 68 */ 69 kMsArgsToStkPass, 70 /* The red zone stack area will not be modified by the exception signal. */ 71 kMsRedZone, 72 }; 73 74 enum StackProtectKind : uint8 { 75 kNone = 0, 76 kAddrofStack = 0x1, 77 /* if a callee has return agg type which size over 16bytes */ 78 kRetureStackSlot = 0x2, 79 }; 80 81 class CGFunc; 82 83 /* keeps track of the allocation of a memory segment */ 84 class MemSegment { 85 public: MemSegment(MemSegmentKind memSegKind)86 explicit MemSegment(MemSegmentKind memSegKind) : kind(memSegKind), size(0) {} 87 88 ~MemSegment() = default; 89 GetSize()90 uint32 GetSize() const 91 { 92 return size; 93 } 94 SetSize(uint32 memSize)95 void SetSize(uint32 memSize) 96 { 97 size = memSize; 98 } 99 GetMemSegmentKind()100 MemSegmentKind GetMemSegmentKind() const 101 { 102 return kind; 103 } 104 105 private: 106 MemSegmentKind kind; 107 uint32 size; /* size is negative if allocated offsets are negative */ 108 }; /* class MemSegment */ 109 110 /* describes where a symbol is allocated */ 111 class SymbolAlloc { 112 public: 113 SymbolAlloc() = default; 114 115 ~SymbolAlloc() = default; 116 GetMemSegment()117 const MemSegment *GetMemSegment() const 118 { 119 return memSegment; 120 } 121 SetMemSegment(const MemSegment & memSeg)122 void SetMemSegment(const MemSegment &memSeg) 123 { 124 memSegment = &memSeg; 125 } 126 GetOffset()127 int64 GetOffset() const 128 { 129 return offset; 130 } 131 SetOffset(int64 off)132 void SetOffset(int64 off) 133 { 134 offset = off; 135 } 136 137 protected: 138 const MemSegment *memSegment = nullptr; 139 int64 offset = 0; 140 }; /* class SymbolAlloc */ 141 142 class MemLayout { 143 public: MemLayout(BECommon & beCommon,MIRFunction & mirFunc,MapleAllocator & mallocator,uint32 kStackPtrAlignment)144 MemLayout(BECommon &beCommon, MIRFunction &mirFunc, MapleAllocator &mallocator, uint32 kStackPtrAlignment) 145 : be(beCommon), 146 mirFunction(&mirFunc), 147 segArgsStkPassed(kMsArgsStkPassed), 148 segArgsRegPassed(kMsArgsRegPassed), 149 segArgsToStkPass(kMsArgsToStkPass), 150 symAllocTable(mallocator.Adapter()), 151 spillLocTable(mallocator.Adapter()), 152 spillRegLocMap(mallocator.Adapter()), 153 localRefLocMap(std::less<StIdx>(), mallocator.Adapter()), 154 memAllocator(&mallocator), 155 stackPtrAlignment(kStackPtrAlignment) 156 { 157 symAllocTable.resize(mirFunc.GetSymTab()->GetSymbolTableSize()); 158 } 159 160 virtual ~MemLayout() = default; 161 SetCurrFunction(CGFunc & func)162 void SetCurrFunction(CGFunc &func) 163 { 164 cgFunc = &func; 165 } 166 167 /* 168 * Returns stack space required for a call 169 * which is used to pass arguments that cannot be 170 * passed through registers 171 */ 172 virtual uint32 ComputeStackSpaceRequirementForCall(StmtNode &stmtNode, int32 &aggCopySize, bool isIcall) = 0; 173 174 /* 175 * Go over all outgoing calls in the function body and get the maximum space 176 * needed for storing the actuals based on the actual parameters and the ABI. 177 * These are usually those arguments that cannot be passed 178 * through registers because a call passes more than 8 arguments, or 179 * they cannot be fit in a pair of registers. 180 */ 181 uint32 FindLargestActualArea(int32 &aggCopySize); 182 183 virtual void LayoutStackFrame(int32 &structCopySize, int32 &maxParmStackSize) = 0; 184 185 /* 186 * "Pseudo-registers can be regarded as local variables of a 187 * primitive type whose addresses are never taken" 188 */ 189 virtual void AssignSpillLocationsToPseudoRegisters() = 0; 190 GetSymAllocInfo(uint32 stIdx)191 SymbolAlloc *GetSymAllocInfo(uint32 stIdx) 192 { 193 DEBUG_ASSERT(stIdx < symAllocTable.size(), "out of symAllocTable's range"); 194 return symAllocTable[stIdx]; 195 } 196 SetSymAllocInfo(uint32 stIdx,SymbolAlloc & symAlloc)197 void SetSymAllocInfo(uint32 stIdx, SymbolAlloc &symAlloc) 198 { 199 DEBUG_ASSERT(stIdx < symAllocTable.size(), "out of symAllocTable's range"); 200 symAllocTable[stIdx] = &symAlloc; 201 } 202 GetSpillLocOfPseduoRegister(PregIdx index)203 const SymbolAlloc *GetSpillLocOfPseduoRegister(PregIdx index) const 204 { 205 return spillLocTable.at(index); 206 } 207 AssignLocationToSpillReg(regno_t vrNum,uint32 memByteSize)208 SymbolAlloc *AssignLocationToSpillReg(regno_t vrNum, uint32 memByteSize) 209 { 210 auto *symLoc = CreateSymbolAlloc(); 211 symLoc->SetMemSegment(segSpillReg); 212 segSpillReg.SetSize(static_cast<uint32>(RoundUp(segSpillReg.GetSize(), memByteSize))); 213 symLoc->SetOffset(segSpillReg.GetSize()); 214 segSpillReg.SetSize(segSpillReg.GetSize() + memByteSize); 215 SetSpillRegLocInfo(vrNum, *symLoc); 216 return symLoc; 217 } 218 GetLocOfSpillRegister(regno_t vrNum,uint32 memByteSize)219 SymbolAlloc *GetLocOfSpillRegister(regno_t vrNum, uint32 memByteSize) 220 { 221 SymbolAlloc *loc = nullptr; 222 auto pos = spillRegLocMap.find(vrNum); 223 if (pos == spillRegLocMap.end()) { 224 loc = AssignLocationToSpillReg(vrNum, memByteSize); 225 } else { 226 loc = pos->second; 227 } 228 return loc; 229 } 230 SizeOfArgsToStackPass()231 uint32 SizeOfArgsToStackPass() const 232 { 233 return segArgsToStkPass.GetSize(); 234 } 235 SizeOfArgsRegisterPassed()236 uint32 SizeOfArgsRegisterPassed() const 237 { 238 return segArgsRegPassed.GetSize(); 239 } 240 GetBECommon()241 BECommon &GetBECommon() 242 { 243 return be; 244 } 245 GetMIRFunction()246 MIRFunction *GetMIRFunction() 247 { 248 return mirFunction; 249 } 250 GetSegArgsStkPassed()251 const MemSegment &GetSegArgsStkPassed() const 252 { 253 return segArgsStkPassed; 254 } 255 GetSegArgsRegPassed()256 const MemSegment &GetSegArgsRegPassed() const 257 { 258 return segArgsRegPassed; 259 } 260 GetSegArgsToStkPass()261 const MemSegment &GetSegArgsToStkPass() const 262 { 263 return segArgsToStkPass; 264 } 265 GetSymAllocTable()266 const MapleVector<SymbolAlloc *> &GetSymAllocTable() const 267 { 268 return symAllocTable; 269 } 270 SetSpillRegLocInfo(regno_t regNum,SymbolAlloc & symAlloc)271 void SetSpillRegLocInfo(regno_t regNum, SymbolAlloc &symAlloc) 272 { 273 spillRegLocMap[regNum] = &symAlloc; 274 } 275 GetLocalRefLocMap()276 const MapleMap<StIdx, SymbolAlloc *> &GetLocalRefLocMap() const 277 { 278 return localRefLocMap; 279 } 280 SetLocalRegLocInfo(StIdx idx,SymbolAlloc & symAlloc)281 void SetLocalRegLocInfo(StIdx idx, SymbolAlloc &symAlloc) 282 { 283 localRefLocMap[idx] = &symAlloc; 284 } 285 IsLocalRefLoc(const MIRSymbol & symbol)286 bool IsLocalRefLoc(const MIRSymbol &symbol) const 287 { 288 return localRefLocMap.find(symbol.GetStIdx()) != localRefLocMap.end(); 289 } 290 GetStackPtrAlignment()291 uint32 GetStackPtrAlignment() const 292 { 293 return stackPtrAlignment; 294 } 295 296 protected: 297 BECommon &be; 298 MIRFunction *mirFunction; 299 MemSegment segArgsStkPassed; 300 MemSegment segArgsRegPassed; 301 MemSegment segArgsToStkPass; 302 MemSegment segSpillReg = MemSegment(kMsSpillReg); 303 MapleVector<SymbolAlloc *> symAllocTable; /* index is stindex from StIdx */ 304 MapleVector<SymbolAlloc *> spillLocTable; /* index is preg idx */ 305 MapleUnorderedMap<regno_t, SymbolAlloc *> spillRegLocMap; 306 MapleMap<StIdx, SymbolAlloc *> localRefLocMap; /* localrefvar formals. real address passed in stack. */ 307 MapleAllocator *memAllocator; 308 CGFunc *cgFunc = nullptr; 309 const uint32 stackPtrAlignment; 310 virtual SymbolAlloc *CreateSymbolAlloc() const = 0; 311 }; 312 } /* namespace maplebe */ 313 314 #endif /* MAPLEBE_INCLUDE_CG_MEMLAYOUT_H */ 315