1 /** 2 * Manage the memory allocated on the runtime stack to save Common Subexpressions (CSE). 3 * 4 * Compiler implementation of the 5 * $(LINK2 https://www.dlang.org, D programming language). 6 * 7 * Copyright: Copyright (C) 1985-1998 by Symantec 8 * Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved 9 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) 10 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 11 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/cgcse.d, backend/cgcse.d) 12 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/cgcse.d 13 */ 14 15 module dmd.backend.cgcse; 16 17 import core.stdc.stdio; 18 import core.stdc.stdlib; 19 import core.stdc.string; 20 21 import dmd.backend.cc; 22 import dmd.backend.cdef; 23 import dmd.backend.code; 24 import dmd.backend.code_x86; 25 import dmd.backend.el; 26 import dmd.backend.global; 27 import dmd.backend.ty; 28 29 import dmd.backend.barray; 30 31 nothrow: 32 @safe: 33 34 /**************************** 35 * Table of common subexpressions stored on the stack. 36 * csextab[] array of info on saved CSEs 37 * CSEpe pointer to saved elem 38 * CSEregm mask of register that was saved (so for multi- 39 * register variables we know which part we have) 40 */ 41 42 enum CSEload = 1; // set if the CSE was ever loaded 43 enum CSEsimple = 2; // CSE can be regenerated easily 44 45 struct CSE 46 { 47 elem* e; // pointer to elem 48 code csimple; // if CSEsimple, this is the code to regenerate it 49 regm_t regm; // mask of register stored there 50 int slot; // slot number 51 ubyte flags; // flag bytes 52 53 nothrow: 54 55 /************************ 56 * Initialize at function entry. 57 */ 58 @trusted 59 static void initialize() 60 { 61 csextab.setLength(64); // reserve some space 62 } 63 64 /************************ 65 * Start for generating code for this function. 66 * After ending generation, call finish(). 67 */ 68 @trusted 69 static void start() 70 { 71 csextab.setLength(0); // no entries in table yet 72 slotSize = REGSIZE; 73 alignment_ = REGSIZE; 74 } 75 76 /******************************* 77 * Create and add a new CSE entry. 78 * Returns: 79 * pointer to created entry 80 */ 81 @trusted 82 static CSE* add() 83 { 84 foreach (ref cse; csextab) 85 { 86 if (cse.e == null) // can share with previously used one 87 { 88 cse.flags &= CSEload; 89 return &cse; 90 } 91 } 92 93 // create new one 94 const slot = cast(int)csextab.length; 95 CSE cse; 96 cse.slot = slot; 97 csextab.push(cse); 98 return &csextab[slot]; 99 } 100 101 /******************************** 102 * Update slot size and alignment to worst case. 103 * 104 * A bit wasteful of stack space. 105 * Params: e = elem with a size and an alignment 106 */ 107 @trusted 108 static void updateSizeAndAlign(elem* e) 109 { 110 if (I16) 111 return; 112 const sz = tysize(e.Ety); 113 if (slotSize < sz) 114 slotSize = sz; 115 const alignsize = el_alignsize(e); 116 117 static if (0) 118 printf("set slot size = %d, sz = %d, al = %d, ty = x%x, %s\n", 119 slotSize, cast(int)sz, cast(int)alignsize, 120 cast(int)tybasic(e.Ety), funcsym_p.Sident.ptr); 121 122 if (alignsize >= 16 && TARGET_STACKALIGN >= 16) 123 { 124 alignment_ = alignsize; 125 STACKALIGN = alignsize; 126 enforcealign = true; 127 } 128 } 129 130 /**** 131 * Get range of all CSEs filtered by matching `e`, 132 * starting with most recent. 133 * Params: e = elem to match 134 * Returns: 135 * input range 136 */ 137 @trusted 138 static auto filter(const elem* e) 139 { 140 struct Range 141 { 142 const elem* e; 143 int i; 144 145 nothrow: 146 147 bool empty() 148 { 149 while (i) 150 { 151 if (csextab[i - 1].e == e) 152 return false; 153 --i; 154 } 155 return true; 156 } 157 158 ref CSE front() { return csextab[i - 1]; } 159 160 void popFront() { --i; } 161 } 162 163 return Range(e, cast(int)csextab.length); 164 } 165 166 /********************* 167 * Remove instances of `e` from CSE table. 168 * Params: e = elem to remove 169 */ 170 @trusted 171 static void remove(const elem* e) 172 { 173 foreach (ref cse; csextab[]) 174 { 175 if (cse.e == e) 176 cse.e = null; 177 } 178 } 179 180 /************************ 181 * Create mask of registers from CSEs that refer to `e`. 182 * Params: e = elem to match 183 * Returns: 184 * mask 185 */ 186 @trusted 187 static regm_t mask(const elem* e) 188 { 189 regm_t result = 0; 190 foreach (ref cse; csextab[]) 191 { 192 if (cse.e) 193 elem_debug(cse.e); 194 if (cse.e == e) 195 result |= cse.regm; 196 } 197 return result; 198 } 199 200 /*** 201 * Finish generating code for this function. 202 * 203 * Get rid of unused cse temporaries by shrinking the array. 204 * References: loaded() 205 */ 206 @trusted 207 static void finish() 208 { 209 while (csextab.length != 0 && (csextab[csextab.length - 1].flags & CSEload) == 0) 210 csextab.setLength(csextab.length - 1); 211 } 212 213 /**** The rest of the functions can be called only after finish() ****/ 214 215 /****************** 216 * Returns: 217 * total size used by CSE's 218 */ 219 @trusted 220 static uint size() 221 { 222 return cast(uint)csextab.length * CSE.slotSize; 223 } 224 225 /********************* 226 * Returns: 227 * alignment needed for CSE region of the stack 228 */ 229 @trusted 230 static uint alignment() 231 { 232 return alignment_; 233 } 234 235 /// Returns: offset of slot i from start of CSE region 236 @trusted 237 static uint offset(int i) 238 { 239 return i * slotSize; 240 } 241 242 /// Returns: true if CSE was ever loaded 243 @trusted 244 static bool loaded(int i) 245 { 246 return i < csextab.length && // array could be shrunk for non-CSEload entries 247 (csextab[i].flags & CSEload); 248 } 249 250 private: 251 __gshared: 252 Barray!CSE csextab; // CSE table (allocated for each function) 253 uint slotSize; // size of each slot in table 254 uint alignment_; // alignment for the table 255 } 256 257 258 /******************** 259 * The above implementation of CSE is inefficient: 260 * 1. the optimization to not store CSE's that are never reloaded is based on the slot number, 261 * not the CSE. This means that when a slot is shared among multiple CSEs, it is treated 262 * as "reloaded" even if only one of the CSEs in that slot is reloaded. 263 * 2. updateSizeAndAlign should only be run when reloading when (1) is fixed. 264 * 3. all slots are aligned to worst case alignment of any slot. 265 * 4. unused slots still get memory allocated to them if they aren't at the end of the 266 * slot table. 267 * 268 * The slot number should be unique to each CSE, and allocation of actual slots should be 269 * done after the code is generated, not during generation. 270 */