1 /** 2 Copyright: Copyright (c) 2018-2019 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 7 /// 8 module vox.be.stack_layout; 9 10 import std.stdio; 11 import std.string : format; 12 import vox.all; 13 14 // Stack Allocation 15 // https://docs.microsoft.com/en-us/cpp/build/stack-allocation?view=vs-2017 16 17 // Before prolog our layout is this 18 // ++ slot index 19 // ???? paramN PN-1 rsp + 8 + N*8 20 // ???? ... 21 // XXZ0 param5 P5 rsp + 40 22 // XXY8 param4 S3 rsp + 32 \ 23 // XXY0 param3 S2 rsp + 24 \ shadow space 24 // XXX8 param2 S1 rsp + 16 / 25 // XXX0 param1 S0 rsp + 8 / aligned to 16 bytes 26 // XXW8 ret addr rsp + 0 <-- RSP 27 // -- 28 29 // Shadow space is prioritized for first 4 arguments 30 // After allocating local stack space 31 // ++ slot index 32 // ???? paramN PN-1 rsp + 8 + N*8 N parameters 33 // ???? ... 34 // XXZ0 param5 P5 rsp + 72 35 // XXY8 shadow3 S3 rsp + 64 \ 36 // XXY0 shadow2 S2 rsp + 56 \ shadow space 37 // XXX8 shadow1 S1 rsp + 48 / 38 // XXX0 shadow0 S0 rsp + 40 / aligned to 16 bytes 39 // XXW8 ret addr rsp + 32 40 // XXW0 local0 L0 rsp + 24 \ 41 // XXV8 local1 L1 rsp + 16 \ numLocals = 4 (example) 42 // XXV0 local2 L2 rsp + 8 / 43 // XXU8 local3 L3 rsp + 0 / <-- RSP 44 // optional padding <-- RSP 45 // -- 46 // all space after ret addr is of size 'lir.stackFrameSize' 47 48 enum STACK_ITEM_SIZE = 8; // x86_64 49 50 /// Arranges items on the stack according to calling convention 51 void pass_stack_layout(CompilationContext* context, FunctionDeclNode* func) 52 { 53 if (func.isExternal) return; 54 55 auto lir = context.getAst!IrFunction(func.backendData.lirData); 56 57 CallConv* callConv = lir.getCallConv(context); 58 IrIndex baseReg = IrIndex(callConv.stackPointer, ArgType.QWORD); 59 60 // 1, 2, 4, 8, 16 61 uint[5] numAlignments; 62 uint[5] alignmentSizes; 63 uint[5] alignmentOffsets; 64 65 lir.stackFrameSize = 0; 66 67 foreach (i, ref StackSlot slot; lir.stackSlots) 68 { 69 if (slot.isParameter) continue; 70 71 uint alignPow = slot.sizealign.alignmentPower; 72 context.assertf(alignPow <= 4, "Big alignments (> 16) aren't implemented"); 73 74 ++numAlignments[alignPow]; 75 alignmentSizes[alignPow] += slot.sizealign.size; 76 lir.stackFrameSize += slot.sizealign.size; 77 } 78 //writefln("reservedBytes1 0x%X", lir.stackFrameSize); 79 80 if (context.useFramePointer) 81 { 82 // ++ varIndex 83 // shadow4 \ 84 // shadow3 \ shadow space 85 // param2 1 \ / 86 // param1 0 rbp + 2 / numParams = 2 / this address is aligned to 16 bytes 87 // ret addr rbp + 1 88 // rbp <-- rbp + 0 frame pointer 89 // saved r0 90 // saved r1 91 // opt pad 92 // local1 2 rbp - 1 \ 93 // local2 3 rbp - 2 / numLocals = 2 94 // -- 95 // baseReg = IrIndex(callConv.framePointer, ArgType.QWORD); // TODO: offset must be relative to frame pointer 96 // frame pointer is stored together with locals 97 lir.stackFrameSize += STACK_ITEM_SIZE; 98 } 99 //writefln("reservedBytes2 0x%X", lir.stackFrameSize); 100 101 alignmentOffsets[4] = 0; 102 foreach_reverse (i; 0..4) 103 { 104 alignmentOffsets[i] = alignmentOffsets[i+1] + alignmentSizes[i+1]; 105 } 106 107 // align to 8 bytes first 108 lir.stackFrameSize = alignValue(lir.stackFrameSize, STACK_ITEM_SIZE); 109 //writefln("reservedBytes3 0x%X", lir.stackFrameSize); 110 111 if (lir.numCalls != 0) { 112 // Align to 16 bytes when we have calls to other functions 113 // Before we are called, the stack is aligned to 16 bytes, after call return address is pushed 114 // We take into account the return address (extra 8 bytes) 115 if ((lir.stackFrameSize + STACK_ITEM_SIZE) % 16 != 0) { 116 lir.stackFrameSize += STACK_ITEM_SIZE; 117 } 118 } 119 //writefln("reservedBytes4 0x%X", lir.stackFrameSize); 120 121 uint paramsOffset = lir.stackFrameSize + STACK_ITEM_SIZE; // locals size + ret addr 122 if (callConv.hasShadowSpace) paramsOffset += 32; 123 124 // TODO utilize shadow space 125 // TODO utilize red zone 126 127 int nextLocalIndex = 0; 128 foreach (i, ref StackSlot slot; lir.stackSlots) 129 { 130 if (slot.isParameter) 131 { 132 // ABI lowering code inserts correct offset from the start of stack arguments 133 slot.displacement = paramsOffset + slot.displacement; 134 } 135 else 136 { 137 uint index = slot.sizealign.alignmentPower; 138 alignmentSizes[index] -= slot.sizealign.size; 139 slot.displacement = alignmentOffsets[index] + alignmentSizes[index]; 140 } 141 slot.baseReg = baseReg; 142 } 143 144 if (context.printStackLayout) lir.dumpStackSlots(context); 145 }