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