1 /* 2 * Copyright (c) 2016 Thomas Pornin <pornin@bolet.org> 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining 5 * a copy of this software and associated documentation files (the 6 * "Software"), to deal in the Software without restriction, including 7 * without limitation the rights to use, copy, modify, merge, publish, 8 * distribute, sublicense, and/or sell copies of the Software, and to 9 * permit persons to whom the Software is furnished to do so, subject to 10 * the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be 13 * included in all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 25 using System; 26 using System.Collections.Generic; 27 28 /* 29 * The implementation for interpreted words. 30 */ 31 32 class WordInterpreted : Word { 33 34 /* 35 * Get the number of local variables for this word. 36 */ 37 internal int NumLocals { 38 get; private set; 39 } 40 41 /* 42 * Get the sequence of opcodes for this word. 43 */ 44 internal Opcode[] Code { 45 get; private set; 46 } 47 48 string[] toResolve; 49 WordInterpreted(T0Comp owner, string name, int numLocals, Opcode[] code, string[] toResolve)50 internal WordInterpreted(T0Comp owner, string name, 51 int numLocals, Opcode[] code, string[] toResolve) 52 : base(owner, name) 53 { 54 this.Code = code; 55 this.toResolve = toResolve; 56 NumLocals = numLocals; 57 } 58 Resolve()59 internal override void Resolve() 60 { 61 if (toResolve == null) { 62 return; 63 } 64 for (int i = 0; i < toResolve.Length; i ++) { 65 string tt = toResolve[i]; 66 if (tt == null) { 67 continue; 68 } 69 Code[i].ResolveTarget(TC.Lookup(tt)); 70 } 71 toResolve = null; 72 } 73 Run(CPU cpu)74 internal override void Run(CPU cpu) 75 { 76 Resolve(); 77 cpu.Enter(Code, NumLocals); 78 } 79 GetReferences()80 internal override List<Word> GetReferences() 81 { 82 Resolve(); 83 List<Word> r = new List<Word>(); 84 foreach (Opcode op in Code) { 85 Word w = op.GetReference(TC); 86 if (w != null) { 87 r.Add(w); 88 } 89 } 90 return r; 91 } 92 GetDataBlocks()93 internal override List<ConstData> GetDataBlocks() 94 { 95 Resolve(); 96 List<ConstData> r = new List<ConstData>(); 97 foreach (Opcode op in Code) { 98 ConstData cd = op.GetDataBlock(TC); 99 if (cd != null) { 100 r.Add(cd); 101 } 102 } 103 return r; 104 } 105 GenerateCodeElements(List<CodeElement> dst)106 internal override void GenerateCodeElements(List<CodeElement> dst) 107 { 108 Resolve(); 109 int n = Code.Length; 110 CodeElement[] gcode = new CodeElement[n]; 111 for (int i = 0; i < n; i ++) { 112 gcode[i] = Code[i].ToCodeElement(); 113 } 114 for (int i = 0; i < n; i ++) { 115 Code[i].FixUp(gcode, i); 116 } 117 dst.Add(new CodeElementUInt((uint)NumLocals)); 118 for (int i = 0; i < n; i ++) { 119 dst.Add(gcode[i]); 120 } 121 } 122 123 int flowAnalysis; 124 int maxDataStack; 125 int maxReturnStack; 126 MergeSA(int[] sa, int j, int c)127 bool MergeSA(int[] sa, int j, int c) 128 { 129 if (sa[j] == Int32.MinValue) { 130 sa[j] = c; 131 return true; 132 } else if (sa[j] != c) { 133 throw new Exception(string.Format( 134 "In word '{0}', offset {1}:" 135 + " stack action mismatch ({2} / {3})", 136 Name, j, sa[j], c)); 137 } else { 138 return false; 139 } 140 } 141 AnalyseFlow()142 internal override void AnalyseFlow() 143 { 144 switch (flowAnalysis) { 145 case 0: 146 break; 147 case 1: 148 return; 149 default: 150 throw new Exception("recursive call detected in '" 151 + Name + "'"); 152 } 153 flowAnalysis = 2; 154 int n = Code.Length; 155 int[] sa = new int[n]; 156 for (int i = 0; i < n; i ++) { 157 sa[i] = Int32.MinValue; 158 } 159 sa[0] = 0; 160 int[] toExplore = new int[n]; 161 int tX = 0, tY = 0; 162 int off = 0; 163 164 int exitSA = Int32.MinValue; 165 int mds = 0; 166 int mrs = 0; 167 168 int maxDepth = 0; 169 170 for (;;) { 171 Opcode op = Code[off]; 172 bool mft = op.MayFallThrough; 173 int c = sa[off]; 174 int a; 175 if (op is OpcodeCall) { 176 Word w = op.GetReference(TC); 177 w.AnalyseFlow(); 178 SType se = w.StackEffect; 179 if (!se.IsKnown) { 180 throw new Exception(string.Format( 181 "call from '{0}' to '{1}'" 182 + " with unknown stack effect", 183 Name, w.Name)); 184 } 185 if (se.NoExit) { 186 mft = false; 187 a = 0; 188 } else { 189 a = se.DataOut - se.DataIn; 190 } 191 mds = Math.Max(mds, c + w.MaxDataStack); 192 mrs = Math.Max(mrs, w.MaxReturnStack); 193 maxDepth = Math.Min(maxDepth, c - se.DataIn); 194 } else if (op is OpcodeRet) { 195 if (exitSA == Int32.MinValue) { 196 exitSA = c; 197 } else if (exitSA != c) { 198 throw new Exception(string.Format( 199 "'{0}': exit stack action" 200 + " mismatch: {1} / {2}" 201 + " (offset {3})", 202 Name, exitSA, c, off)); 203 } 204 a = 0; 205 } else { 206 a = op.StackAction; 207 mds = Math.Max(mds, c + a); 208 } 209 c += a; 210 maxDepth = Math.Min(maxDepth, c); 211 212 int j = op.JumpDisp; 213 if (j != 0) { 214 j += off + 1; 215 toExplore[tY ++] = j; 216 MergeSA(sa, j, c); 217 } 218 off ++; 219 if (!mft || !MergeSA(sa, off, c)) { 220 if (tX < tY) { 221 off = toExplore[tX ++]; 222 } else { 223 break; 224 } 225 } 226 } 227 228 maxDataStack = mds; 229 maxReturnStack = 1 + NumLocals + mrs; 230 231 /* 232 * TODO: see about this warning. Usage of a 'fail' 233 * word (that does not exit) within a 'case..endcase' 234 * structure will make an unreachable opcode. In a future 235 * version we might want to automatically remove dead 236 * opcodes. 237 for (int i = 0; i < n; i ++) { 238 if (sa[i] == Int32.MinValue) { 239 Console.WriteLine("warning: word '{0}'," 240 + " offset {1}: unreachable opcode", 241 Name, i); 242 continue; 243 } 244 } 245 */ 246 247 SType computed; 248 if (exitSA == Int32.MinValue) { 249 computed = new SType(-maxDepth, -1); 250 } else { 251 computed = new SType(-maxDepth, -maxDepth + exitSA); 252 } 253 254 if (StackEffect.IsKnown) { 255 if (!computed.IsSubOf(StackEffect)) { 256 throw new Exception(string.Format( 257 "word '{0}':" 258 + " computed stack effect {1}" 259 + " does not match declared {2}", 260 Name, computed.ToString(), 261 StackEffect.ToString())); 262 } 263 } else { 264 StackEffect = computed; 265 } 266 267 flowAnalysis = 1; 268 } 269 270 internal override int MaxDataStack { 271 get { 272 AnalyseFlow(); 273 return maxDataStack; 274 } 275 } 276 277 internal override int MaxReturnStack { 278 get { 279 AnalyseFlow(); 280 return maxReturnStack; 281 } 282 } 283 } 284