protected IEnumerable<Opcode> BuildBoilerplateLoader() { List<Opcode> boilerplate = new List<Opcode>(); InternalPath path = new BuiltInPath(); // First label of the load/runner will be called "@LR00", which is important because that's // what we hardcode all the compiler-built VisitRunStatement's to call out to: labelCounter = 0; labelFormat = "@LR{0:D2}"; boilerplate.Add(new OpcodePushScope(-999,0) {Label = nextLabel, SourcePath = path}); // High level kerboscript function calls flip the argument orders for us, but // low level kRISC does not so the parameters have to be read in stack order: // store parameter 2 in a local name: boilerplate.Add(new OpcodePush("$runonce") {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeSwap() {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeStoreLocal() {Label = nextLabel, SourcePath = path}); // store parameter 1 in a local name: boilerplate.Add(new OpcodePush("$filename") {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeSwap() {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeStoreLocal() {Label = nextLabel, SourcePath = path}); // Unconditionally call load() no matter what. load() will abort and return // early if the program was already compiled, and tell us that on the stack: boilerplate.Add(new OpcodePush(new kOS.Safe.Execution.KOSArgMarkerType()) {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodePush("$filename") {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeEval() {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodePush(true) {Label = nextLabel, SourcePath = path}); // the flag that tells load() to abort early if it's already loaded: boilerplate.Add(new OpcodePush(null) {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeCall("load()") {Label = nextLabel, SourcePath = path}); // Stack now has the 2 return values of load(): // Topmost value is a boolean flag for whether or not the program was already loaded. // Second-from-top value is the entry point into the program. // If load() didn't claim the program was already loaded, or if we aren't operating // in "once" mode, then jump to the part where we call the loaded program, else // fall through to a dummy do-nothing return for the "run once, but it already ran" case: Opcode branchFromOne = new OpcodeBranchIfFalse() {Label = nextLabel, SourcePath = path}; boilerplate.Add(branchFromOne); boilerplate.Add(new OpcodePush("$runonce") {Label = nextLabel, SourcePath = path}); Opcode branchFromTwo = new OpcodeBranchIfFalse() {Label = nextLabel, SourcePath = path}; boilerplate.Add(branchFromTwo); boilerplate.Add(new OpcodePop() {Label = nextLabel, SourcePath = path}); // onsume the entry point that load() returned. We won't be calling it. boilerplate.Add(new OpcodePush(0) {Label = nextLabel, SourcePath = path}); // ---+-- The dummy do-nothing return. boilerplate.Add(new OpcodeReturn(1) {Label = nextLabel, SourcePath = path}); // ---' // Actually call the Program from its entry Point, which is now the thing left on top // of the stack from the second return value of load(): Opcode branchTo = new OpcodePush("$entrypoint") {Label = nextLabel, SourcePath = path}; boilerplate.Add(branchTo); boilerplate.Add(new OpcodeSwap() {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeStoreLocal() {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeCall("$entrypoint") {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodePop() {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodePush(0) {Label = nextLabel, SourcePath = path}); boilerplate.Add(new OpcodeReturn(1) {Label = nextLabel, SourcePath = path}); branchFromOne.DestinationLabel = branchTo.Label; branchFromTwo.DestinationLabel = branchTo.Label; return boilerplate; }
private void ReplaceLabels(List<Opcode> program) { var labels = new Dictionary<string, int>(); // get the index of every label for (int index = 0; index < program.Count; index++) { if (program[index].Label != string.Empty) { if (labels.ContainsKey(program[index].Label)) { if (program[index].Label.EndsWith("-default")) continue; // This is one of those "should never happen" errors that if it happens // it means kOS devs screwed up - so dump the partially relabeled program // to the log just to help in diagnosing the bug report that may happen: // Utilities.SafeHouse.Logger.LogError("=====Relabeled Program so far is: ========="); Utilities.SafeHouse.Logger.LogError(Utilities.Debug.GetCodeFragment(program)); throw new Exceptions.KOSCompileException(LineCol.Unknown(), string.Format( "ProgramBuilder.ReplaceLabels: Cannot add label {0}, label already exists. Opcode: {1}", program[index].Label, program[index].ToString())); } labels.Add(program[index].Label, index); } } // replace destination labels with the corresponding index for (int index = 0; index < program.Count; index++) { Opcode opcode = program[index]; if (string.IsNullOrEmpty(opcode.DestinationLabel)) continue; if (!labels.ContainsKey(opcode.DestinationLabel)) { Utilities.SafeHouse.Logger.LogError("=====Relabeled Program so far is: ========="); Utilities.SafeHouse.Logger.LogError(Utilities.Debug.GetCodeFragment(program)); throw new Exceptions.KOSCompileException(LineCol.Unknown(), string.Format( "ProgramBuilder.ReplaceLabels: Cannot find label {0}. Opcode: {1}", opcode.DestinationLabel, opcode.ToString())); } int destinationIndex = labels[opcode.DestinationLabel]; if (opcode is BranchOpcode) { ((BranchOpcode)opcode).Distance = destinationIndex - index; } else if (opcode is OpcodePushRelocateLater) { // Replace the OpcodePushRelocateLater with the proper OpcodePush: Opcode newOp; if (opcode is OpcodePushDelegateRelocateLater) { newOp = new OpcodePushDelegate(destinationIndex, ((OpcodePushDelegateRelocateLater)opcode).WithClosure); } else newOp = new OpcodePush(destinationIndex); newOp.SourcePath = opcode.SourcePath; newOp.SourceLine = opcode.SourceLine; newOp.SourceColumn = opcode.SourceColumn; newOp.Label = opcode.Label; program[index] = newOp; } else if (opcode is OpcodeCall) { ((OpcodeCall)opcode).Destination = destinationIndex; } } // complete the entry point address of all the objects foreach (var objectFile in objectFiles.Values) { if (objectFile.EntryPointLabel != string.Empty) objectFile.EntryPointAddress = labels[objectFile.EntryPointLabel]; } }
private void ReplaceLabels(List<Opcode> program) { var labels = new Dictionary<string, int>(); // get the index of every label for (int index = 0; index < program.Count; index++) { if (program[index].Label != string.Empty) { labels.Add(program[index].Label, index); } } // replace destination labels with the corresponding index for (int index = 0; index < program.Count; index++) { Opcode opcode = program[index]; if (string.IsNullOrEmpty(opcode.DestinationLabel)) continue; int destinationIndex = labels[opcode.DestinationLabel]; if (opcode is BranchOpcode) { ((BranchOpcode)opcode).Distance = destinationIndex - index; } else if (opcode is OpcodePushRelocateLater) { // Replace the OpcodePushRelocateLater with the proper OpcodePush: Opcode newOp; if (opcode is OpcodePushDelegateRelocateLater) { newOp = new OpcodePushDelegate(destinationIndex, ((OpcodePushDelegateRelocateLater)opcode).WithClosure); } else newOp = new OpcodePush(destinationIndex); newOp.SourceName = opcode.SourceName; newOp.SourceLine = opcode.SourceLine; newOp.SourceColumn = opcode.SourceColumn; newOp.Label = opcode.Label; program[index] = newOp; } else if (opcode is OpcodeCall) { ((OpcodeCall)opcode).Destination = destinationIndex; } } // complete the entry point address of all the objects foreach (var objectFile in objectFiles.Values) { if (objectFile.EntryPointLabel != string.Empty) objectFile.EntryPointAddress = labels[objectFile.EntryPointLabel]; } }