public Code(Label value) { this.Type = CodeType.Label; this.Value = value.Index; this.Flags = CodeFlags.None; }
/// <summary> /// Fix the label to the given label. /// </summary> public void Fix(Label fixTo, SourceSpan span) { this.ForwardsTo = fixTo; this.Span = span; }
/// <summary> /// The main entry point into the code generator, this assembles a /// complete, parsed source file. /// </summary> public CgResult CgProgram(ParseTreeNode node) { /* * If it weren't for labels, this would be a piece of cake. All * we'd have to do is produce cpu code for each instruction and * join them all together. * * But we *do* have to deal with labels, so there. We could handle * this by keeping track of a list of fixups; locations that contain * a forward reference. Then, once we know what that label resolves * to, we go back and do the fixups. * * However, I'd like to be able to do optimisation on the generated * code; specifically, replacing jumps to nearby labels with * relative jumps using add/sub pc, X. Doing this will cause the * address of all subsequent code (and labels) to change. * * In that case, it's probably simpler to just leave all labels * unresolved until *after* optimisation. But what happens when * we do shorten a given instruction? * * When we shorten a given instruction, all subsequent fixups need * to have their target address adjusted. * * An attractive alternative is to not keep a list of fixups; * instead, generated code is widened such that we can store * metadata for each word. This would include storing label * references directly. The advantage of this is that moving * code around is cheap. * * So that's what we'll do. * * For now, we'll just use a list to store the generated code. * Ideally, we'd have a linked list of pages since we won't be * doing a lot of cutting. */ var code = new List<Code>(); /* * This array stores difference literals. We need to do this * since difference literals can depend on labels but won't * fit in a code. */ var diffLits = new List<Difference>(); /* * We also need something to keep track of the defined labels. * Since we need to be able to pack this into code entries, * we'll stick them in a flat array. */ var labels = new List<Label>(); /* * A way of mapping a label name to a label object would also * be handy. */ var labelMap = new Dictionary<string, Label>(); /* * In order to implement local labels, we need to track the * last non-local label we saw. */ var lastGlobalLabel = InitialLabel; /* * We'll also need a function we can pass into the codegen * functions to turn a label name into a label object. */ LabelLookup lookup = delegate(string name) { if (name.StartsWith(".")) name = lastGlobalLabel + name; if (labelMap.ContainsKey(name)) return labelMap[name]; else { var index = (ushort)labels.Count; var label = new Label(index, name); labels.Add(label); labelMap[name] = label; return label; } }; /* * And while we're on the subject: a function to write to the * code list. */ CodeWriter write = delegate(Code c) { code.Add(c); }; Func<Difference, Code> diffToCode = delegate(Difference diff) { var index = (ushort)diffLits.Count; diffLits.Add(diff); return new Code(CodeType.Difference, index); }; /* * Whack 'em in a context. */ var ctx = new CgContext { Lookup = lookup, Write = write, GetCurrentAddress = () => (ushort)code.Count, EncodeDifference = diffToCode, }; /* * Ok, let's process those lines. */ foreach (var line in node.ChildNodes) { var labelNode = line.ChildNodes[0].ChildNodes.FirstOrDefault(); var instrNode = line.ChildNodes[1].ChildNodes.FirstOrDefault(); var labelName = labelNode != null ? labelNode.ChildNodes[0].Token.Text : null; if (labelName != null) { if (!labelName.StartsWith(".")) lastGlobalLabel = labelName; var label = lookup(labelName); if (label.Fixed) throw new CodegenException("Label '{0}' already defined at {1}.", labelName, label.Span.Location); if (instrNode != null && instrNode.Term.Name == "AddressFix") { var fix = EvalLiteralWord(instrNode.ChildNodes[0], ref ctx); switch (fix.Type) { case CodeType.Literal: label.Fix(fix.Value, labelNode.Span); break; case CodeType.Label: label.Fix(labels[fix.Value], labelNode.Span); break; case CodeType.Difference: throw new CodegenException("Labels cannot be fixed to difference literals"); default: throw new UnexpectedGrammarException(); } instrNode = null; } else { var addr = (ushort)code.Count; label.Fix(addr, labelNode.Span); } } if (instrNode != null) CgOptInstruction(instrNode, ref ctx); } /* * Let's add a few extra labels for fun. And usefulness. */ ctx.Lookup("__CODE_START").Fix(0, node.Span); ctx.Lookup("__CODE_END").Fix((ushort)code.Count, node.Span); /* * We now have code generated for the whole program. What we * we need to do now is go back and fill in the labels. * * Before that, let's just make sure that all labels were * actually defined somewhere... */ foreach (var label in labels) if (!label.Fixed) throw new CodegenException("Label '{0}' never defined.", label.Name); /* * Right. We now have enough information to generate the final, complete * binary. * * First of all, we'll fill in guesses for what the labels are attached to. */ foreach (var label in labels) { if (label.IsForwarded) continue; var addr = label.Value; if (addr >= code.Count) continue; switch (code[addr].Type) { case CodeType.Instruction: label.Type = LabelType.Code; break; case CodeType.Label: if( (code[addr].Flags & CodeFlags.IsLiteral) == 0 ) goto default; label.Type = LabelType.Data; break; case CodeType.Literal: label.Type = LabelType.Data; break; default: label.Type = LabelType.Unknown; break; } } /* * Now we can produce the output image. */ Func<Code, ushort> evalCode = null; evalCode = delegate(Code c) { switch (c.Type) { case CodeType.Label: return labels[c.Value].Value; case CodeType.Difference: { var diff = diffLits[c.Value]; return (ushort)(evalCode(diff.Target) - evalCode(diff.Base)); } default: return c.Value; } }; var image = new ushort[code.Count]; { var i = 0; foreach (var c in code) { image[i] = evalCode(c); ++i; } } /* * Done. Collect the results. */ labels.Sort((a, b) => a.Value - b.Value); return new CgResult { Image = image, Labels = labels, }; }