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