private void OutputBinaryBranch(BHAVInstruction inst, CSTranslationClass cls) { WriteLine("{"); IndentLevel++; OutputReturnBlockOrJump(inst.TruePointer, cls); IndentLevel--; WriteLine("}"); WriteLine("else"); WriteLine("{"); IndentLevel++; OutputReturnBlockOrJump(inst.FalsePointer, cls); IndentLevel--; WriteLine("}"); }
public CSTranslationClass TranslateBHAV(StructuredBHAV bhav) { var csClass = new CSTranslationClass(); csClass.ClassName = CSTranslationContext.FormatName(bhav.Source.ChunkLabel) + "_" + bhav.Source.ChunkID; csClass.Structure = bhav; csClass.InlineFunction = !bhav.Yields; csClass.ArgCount = Math.Max(4, (int)bhav.Source.Args); //possible to make this tighter by analysing func Context.CurrentBHAV = bhav.Source; Context.CurrentClass = csClass; csClass.Instructions = bhav.Instructions.ToDictionary(x => x.Key, x => { var inst = x.Value; return(new CSTranslationInstruction(inst.Translator.CodeGen(Context), inst.ReturnType, inst.Yields)); }); return(csClass); }
private void OutputReturnBlockOrJump(byte instruction, CSTranslationClass cls) { switch (instruction) { case 255: //return false UpdateInstruction(); WriteLine($"return {cls.FalseExp};"); break; case 254: //return true UpdateInstruction(); WriteLine($"return {cls.TrueExp};"); break; case 253: UpdateInstruction(); WriteLine($"return {cls.ErrorExp};"); break; default: var block = cls.Structure.RootBlock.Body.FirstOrDefault(x => x.StartInstructionIndex == instruction); if (block == null) { goto case 255; } if (block.Parent != cls.Structure.RootBlock) { //ready for a statement block. OutputBlock(block, cls); } else { LastInstruction = instruction; UpdateInstruction(); //jump back into the global switch } break; } }
public void OutputClass(CSTranslationClass cls) { Context.CurrentClass = cls; Indent = GetIndentation(1); WriteLine($"public class {cls.ClassName} : {cls.Interface}"); WriteLine("{"); IndentLevel = 2; WriteLines(cls.PrimitiveDefinitions.Values.ToList(), false); if (cls.PrimitiveDefinitions.Count > 0) { WriteLine(); } WriteLines(cls.OperandDefinitions.Values.ToList(), false); if (cls.OperandDefinitions.Count > 0) { WriteLine(); } if (cls.InlineFunction) { WriteLine($"public override int ArgCount => {cls.ArgCount};"); WriteLine(); } WriteLine(cls.FunctionHead); WriteLine("{"); IndentLevel = 3; WriteLine("bool _bResult;"); WriteLine("VMPrimitiveExitCode _sResult;"); if (cls.UseParams && !cls.InlineFunction) { WriteLine("var args = context.Args;"); } if (cls.InlineFunction) { if (cls.UseLocals) { WriteLine($"var locals = new short[{cls.Structure.Source.Locals}];"); WriteLine($"context.Locals = locals;"); } else { WriteLine($"context.Locals = new short[{cls.Structure.Source.Locals}];"); } } else { if (cls.UseLocals) { WriteLine("var locals = context.Locals;"); } } if (cls.UseTemps) { WriteLine("var temps = context.Thread.TempRegisters;"); } WriteLine(); var root = cls.Structure.RootBlock; var topLevel = root.Body.Where(x => x.Parent == root); var useLoop = topLevel.Count() > 1 || topLevel.FirstOrDefault()?.FromBlocks.Count > 0; if (useLoop) { WriteLine("int _loops = 0;"); WriteLine($"while (++_loops < {Context.MaxLoopCount})"); WriteLine("{"); IndentLevel = 4; } if (topLevel.Count() > 1) { cls.HasGlobalSwitch = true; WriteLine("switch (instruction)"); WriteLine("{"); IndentLevel++; foreach (var seq in topLevel) { WriteLine($"case {seq.StartInstructionIndex}:"); IndentLevel++; OutputBlock(seq, cls); IndentLevel--; } WriteLine("default:"); IndentLevel++; WriteLine("throw new JITMissInstructionException(instruction);"); IndentLevel--; IndentLevel--; WriteLine("}"); } else if (topLevel.Count() == 1) { cls.HasGlobalSwitch = false; OutputBlock(topLevel.First(), cls); } else { cls.HasGlobalSwitch = false; WriteLine($"return {cls.TrueExp};"); } if (useLoop) { IndentLevel = 3; WriteLine("}"); WriteLine("throw new JITLoopException();"); } IndentLevel = 2; WriteLine("}"); IndentLevel = 1; WriteLine("}"); WriteLine(); }
private void OutputBlock(SimanticsBlock block, CSTranslationClass cls) { for (int i = 0; i < block.IBody.Count - 1; i++) { var inst = block.IBody[i]; var body = cls.Instructions[inst.Index]; switch (body.ReturnType) { case PrimitiveReturnType.SimanticsTrue: case PrimitiveReturnType.SimanticsTrueFalse: //truefalse result discarded, same destination WriteLines(body.Body, true); break; case PrimitiveReturnType.NativeStatementTrue: case PrimitiveReturnType.NativeStatementTrueFalse: //truefalse result discarded, same destination WriteLines(body.Body, false); break; case PrimitiveReturnType.NativeExpressionTrueFalse: //we have a problem... the expression needs to evaluate, but we don't do anything with the result. //put it in the boolean temp and ignore it. if (body.Body.Count == 1) { WriteLine($"_bResult = {body.Body.First()};"); } else { WriteLines(body.Body, true); } break; case PrimitiveReturnType.SimanticsSubroutine: //a subroutine that yields but doesn't branch. default: throw new Exception("Non-statement in the middle of a sequence (detected areas with no branching)?"); } } var lastInst = block.IBody[block.IBody.Count - 1]; var lastBody = cls.Instructions[lastInst.Index]; LastInstruction = block.LastInstructionIndex; string caseConst = null; if (block.BlockType == SimanticsBlockType.Switch || block.BlockType == SimanticsBlockType.SwitchCase || block.BlockType == SimanticsBlockType.SwitchLast) { caseConst = CSScopeMemory.GetConstant(Context, block.SwitchOperand.RhsOwner, block.SwitchOperand.RhsData); } switch (block.BlockType) { case SimanticsBlockType.InstructionSequence: case SimanticsBlockType.IfElse: //just set the instruction based on the value and return back to the while loop if (lastBody.ReturnType == PrimitiveReturnType.SimanticsSubroutine || lastInst.Yields) { UpdateInstruction(); if (lastBody.Body.Count > 1) { WriteLines(lastBody.Body, false); WriteLine($"return _sResult;"); } else { WriteLine($"return {lastBody.Body.First()};"); } break; } switch (lastBody.ReturnType) { case PrimitiveReturnType.NativeStatementTrueFalse: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { goto case PrimitiveReturnType.NativeStatementTrue; } WriteLines(lastBody.Body, false); WriteLine("if (_bResult) "); OutputBinaryBranch(lastInst.Instruction, cls); break; case PrimitiveReturnType.NativeExpressionTrueFalse: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { WriteLine($"_bResult = {lastBody.Body.First()}; //true and false do the same thing"); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); } else { WriteLine($"if ({lastBody.Body.First()}) "); OutputBinaryBranch(lastInst.Instruction, cls); } break; case PrimitiveReturnType.SimanticsTrueFalse: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { goto case PrimitiveReturnType.SimanticsTrue; } WriteLine($"if ({lastBody.Body.First()} == VMPrimitiveExitCode.GOTO_TRUE) "); OutputBinaryBranch(lastInst.Instruction, cls); break; case PrimitiveReturnType.SimanticsStatement: if (lastInst.Instruction.TruePointer == lastInst.Instruction.FalsePointer) { WriteLines(lastBody.Body, false); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); } else { WriteLines(lastBody.Body, false); WriteLine($"if (_sResult == VMPrimitiveExitCode.GOTO_TRUE) "); OutputBinaryBranch(lastInst.Instruction, cls); } break; case PrimitiveReturnType.NativeStatementTrue: WriteLines(lastBody.Body, false); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); break; case PrimitiveReturnType.SimanticsTrue: WriteLines(lastBody.Body, true); OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); break; } if (block.Parent == cls.Structure.RootBlock && cls.HasGlobalSwitch) { WriteLine("break;"); } break; case SimanticsBlockType.Switch: string switchLhs = CSScopeMemory.GetExpression(Context, block.SwitchOperand.LhsOwner, block.SwitchOperand.LhsData, true); WriteLine($"switch ({switchLhs}) "); WriteLine("{"); IndentLevel++; WriteLine($"case {caseConst}:"); IndentLevel++; var set = new HashSet <string> { caseConst }; cls.SwitchPreviousCases[block] = set; OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); WriteLine($"break;"); IndentLevel--; OutputReturnBlockOrJump(lastInst.Instruction.FalsePointer, cls); break; case SimanticsBlockType.SwitchCase: var cset = cls.SwitchPreviousCases[FindSwitchBlockStart(block)]; if (cset.Contains(caseConst)) { WriteLine($"// case {caseConst} duplicated! skipped block at {lastInst.Instruction.FalsePointer}"); } else { cset.Add(caseConst); WriteLine($"case {caseConst}:"); IndentLevel++; OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); WriteLine($"break;"); IndentLevel--; } OutputReturnBlockOrJump(lastInst.Instruction.FalsePointer, cls); break; case SimanticsBlockType.SwitchLast: var lset = cls.SwitchPreviousCases[FindSwitchBlockStart(block)]; if (lset.Contains(caseConst)) { WriteLine($"// case {caseConst} duplicated! skipped block at {lastInst.Instruction.FalsePointer}"); } else { lset.Add(caseConst); WriteLine($"case {caseConst}:"); IndentLevel++; OutputReturnBlockOrJump(lastInst.Instruction.TruePointer, cls); WriteLine($"break;"); IndentLevel--; } WriteLine($"default:"); IndentLevel++; OutputReturnBlockOrJump(lastInst.Instruction.FalsePointer, cls); WriteLine($"break;"); IndentLevel--; IndentLevel--; WriteLine("}"); if (FindSwitchBlockStart(block).Parent == cls.Structure.RootBlock && cls.HasGlobalSwitch) { WriteLine("break;"); } break; default: throw new Exception($"Block type {block.BlockType.ToString()} not yet implemented."); } //our last instruction }