public void Emit(CompilationContext context) { context.EmitComment(";Multiplicative expression"); ((ICodeEmitter)Tokens[0]).Emit(context); var t1ExpressionType = ((IHasType)Tokens[0]).GetExpressionType(context); if (Tokens.Count > 1) { TypeChecking.CheckExpressionTypeIsNumeric(t1ExpressionType); } for (int i = 1; i < Tokens.Count; i += 2) { ((ICodeEmitter)Tokens[i + 1]).Emit(context); var t2ExpressionType = ((IHasType)Tokens[i + 1]).GetExpressionType(context); TypeChecking.CheckExpressionTypeIsNumeric(t2ExpressionType); TypeChecking.CheckExpressionTypesMatch(t1ExpressionType, t2ExpressionType); t1ExpressionType = t2ExpressionType; string op = ((DefaultLanguageTerminalToken)Tokens[i]).Value; context.EmitInstruction(new IRPop() { To = "ebx" }); context.EmitInstruction(new IRPop() { To = "eax" }); switch (op) { case "*": context.EmitInstruction(new IRMult() { Left = "eax", Right = "ebx", To = "ecx" }); break; case "/": context.EmitInstruction(new IRDiv() { Left = "eax", Right = "ebx", To = "ecx" }); break; } context.EmitInstruction(new IRPushRegister() { From = "ecx" }); } }
public void Emit(CompilationContext context) { context.EmitComment(";Return statement"); context.ReportReturnStatement(); var returnExpressionType = new ExpressionType() { BaseType = new TypeDef() { Name = "void", Size = 0 } }; if (Tokens.Count > 1) { returnExpressionType = ((IHasType)Tokens[1]).GetExpressionType(context); ((ICodeEmitter)Tokens[1]).Emit(context); //(Caller saves registers) if (returnExpressionType.GetSize() > 4) { //Return value gets placed space allocated in caller's stack throw new Exception("Large return values not supported"); } else { //Return value from function goes in eax context.EmitInstruction(new IRPop() { To = "eax" }); } } int localVarsSize = context.GetFunctionLocalVarSize(); //Reclaim local variables from stack space context.EmitInstruction(new IRMoveImmediate() { To = "ebx", Value = new ImmediateValue(localVarsSize) }); context.EmitInstruction(new IRSub() { To = "sp", Left = "sp", Right = "ebx" }); TypeChecking.CheckExpressionTypesMatch(context.GetCurrentFunctionReturnExpressionType(), returnExpressionType); if (context.IsEntryPointFunction) { //DONT WANT THIS. HALT JUST STOPS CPU COMPLETELY NOW //At this point the return value of the function is still in eax and we simple halt execution //as the exe has run to completion //context.EmitInstruction(new IRHalt()); } else { //Pop return address off stack and jump context.EmitInstruction(new IRRet()); } }
public void Emit(CompilationContext context) { context.EmitComment(";Function call"); var expressionType = ((IHasType)Tokens[0]).GetExpressionType(context); if (!(expressionType.BaseType is FunctionTypeDef)) { throw new Exception("Can't call expression type: " + expressionType + " as a function"); } FunctionTypeDef functionType = (FunctionTypeDef)expressionType.BaseType; ExpressionType returnType = functionType.ReturnType; //List<ExpressionType> parameterTypes = functionType.ArgumentTypes; if (returnType.GetSize() > 4) { //Make space for return value in caller stack //context.EmitInstruction(new IRMoveImmediate() { To = "eax", Value = new ImmediateValue(function.ReturnType.GetSize()) }); //context.EmitInstruction(new IRAdd() { Left = "sp", Right = "eax", To = "sp" }); throw new LargeReturnValuesNotSupportedException(); } //Push base pointer on stack context.EmitInstruction(new IRPushRegister() { From = "bp" }); //Save registers (TODO: Not actually needed until we have smarter allocation that uses registers instead of stack) //context.EmitInstruction(new IRPushRegister() { From = "eax" }); //context.EmitInstruction(new IRPushRegister() { From = "ebx" }); //context.EmitInstruction(new IRPushRegister() { From = "ecx" }); //context.EmitInstruction(new IRPushRegister() { From = "edx" }); int argumentCount = Tokens.Count - 1; int argumentsSize = 0; if (argumentCount != functionType.ArgumentTypes.Count) { throw new ArgumentCountMismatchException(Tokens[0].ToString(), functionType.ArgumentTypes.Count, argumentCount); } if (Tokens.Count > 1) { //Push arguments on stack in reverse order for (int i = Tokens.Count - 1; i > 0; i--) { var argExpressionType = ((IHasType)Tokens[i]).GetExpressionType(context); var paramExpressionType = functionType.ArgumentTypes[functionType.ArgumentTypes.Count - 1 - (Tokens.Count - 1 - i)]; TypeChecking.CheckExpressionTypesMatch(paramExpressionType, argExpressionType); //Push argument value on stack ((ICodeEmitter)Tokens[i]).Emit(context); argumentCount++; argumentsSize += argExpressionType.GetSize(); } } var returnLabel = new LabelAddressValue(context.CreateNewLabel()); //Address of function -> eax ((ICodeEmitter)Tokens[0]).Emit(context); context.EmitInstruction(new IRPop() { To = "eax" }); //Set base pointer to be the top of current function's stack which will be the bottom //of the called function's stack context.EmitInstruction(new IRMoveRegister() { From = "sp", To = "bp" }); //Push return address context.EmitInstruction(new IRPushImmediate() { Value = returnLabel }); //Jump to function context.EmitInstruction(new IRJumpRegister() { Address = "eax" }); //Resume here, reclaim space from arguments pushed on stack context.EmitLabel(returnLabel.Value); context.EmitInstruction(new IRMoveImmediate() { To = "ebx", Value = new ImmediateValue(argumentsSize) }); context.EmitInstruction(new IRSub() { To = "sp", Left = "sp", Right = "ebx" }); //Restore registers (TODO: Not actually needed until we have smarter allocation that uses registers instead of stack) //context.EmitInstruction(new IRPop() { To = "edx" }); //context.EmitInstruction(new IRPop() { To = "ecx" }); //context.EmitInstruction(new IRPop() { To = "ebx" }); //context.EmitInstruction(new IRPop() { To = "eax" }); //Reset base pointer context.EmitInstruction(new IRPop() { To = "bp" }); if (returnType.GetSize() > 4) { //Return value is already on stack throw new LargeReturnValuesNotSupportedException(); } else if (returnType.GetSize() > 0) { //Return value in eax, put on stack context.EmitInstruction(new IRPushRegister() { From = "eax" }); } }
public void Emit(CompilationContext context) { context.EmitComment(";Variable definition"); var type = ((IHasType)Tokens[0]).GetExpressionType(context); string variableName = ((IdentifierToken)Tokens[1]).Name; if (type.IsArray && type.ArrayLength == -1) { throw new MissingArraySizeSpecifierException(variableName); } context.AddVariableSymbol(variableName, type, IsStatic, IsExported, IsExtern); if (!IsStatic && !context.InFunctionScope()) { throw new Exception("Cannot define non static variable outside of function scope."); } if (Tokens.Count > 2) { var assignmentExpressionType = ((IHasType)Tokens[3]).GetExpressionType(context); var variable = context.GetVariable(variableName); if (variable.Type.GetSize() == 0) { throw new VoidAssignmentException("to"); } else if (assignmentExpressionType.GetSize() == 0) { throw new VoidAssignmentException("from"); } TypeChecking.CheckExpressionTypesMatch(variable.Type, assignmentExpressionType); //Special case for assignment of string literal to byte array: // Don't emit string constant normally (which would add it as a string constant in the data section), // instead copy the string to the memory occupied by tge byte array itself if (assignmentExpressionType.BaseType is StringLiteralTypeDef && variable.Type.IsArray) { string stringLiteral = ((StringLiteralTypeDef)assignmentExpressionType.BaseType).Value; if (assignmentExpressionType.ArrayLength > variable.Type.ArrayLength) { throw new Exception("The string '" + stringLiteral + "' is too large to be assigned by value to the left hand expression."); } //Put start address to copy string value to into eax if (variable.Address is StackAddressValue) { context.EmitInstruction(new IRMoveImmediate() { To = "ebx", Value = new ImmediateValue(variable.Address.Value) }); context.EmitInstruction(new IRAdd() { To = "ebx", Left = "bp", Right = "ebx" }); context.EmitInstruction(new IRPushRegister() { From = "ebx" }); } else { context.EmitInstruction(new IRPushImmediate() { Value = variable.Address }); } context.EmitInstruction(new IRPop() { To = "eax" }); for (int i = 0; i < assignmentExpressionType.ArrayLength; i++) { byte charValue = i < stringLiteral.Length ? (byte)stringLiteral[i] : (byte)0; //Put char value into ebx context.EmitInstruction(new IRMoveImmediate() { To = "ebx", Value = new ImmediateValue(charValue), OperandSize = 1 }); //Store char value into address [eax + char offset] context.EmitInstruction(new IRStoreRegisterPlusImmediate() { To = "eax", Offset = new ImmediateValue(i), From = "ebx", OperandSize = 1 }); } } else { if (assignmentExpressionType.GetSize() > 4) { //Memory copy //Dest address -> eax throw new Exception("TODO: Copy large values to stack variables"); //context.EmitInstruction(new IRMoveImmediate() { To = "eax", Value = variable.Address }); //STACK!!!! //Source address -> ebx //((IHasAddress)Tokens[3]).PushAddress(context); //context.EmitInstruction(new IRPop() { To = "ebx" }); //context.EmitInstruction(new IRMemCopy() { From = "ebx", To = "eax", Length = new ImmediateValue(assignmentExpressionType.GetSize()) }); } else { //Copy using register ((ICodeEmitter)Tokens[3]).Emit(context); context.EmitInstruction(new IRPop() { To = "eax" }); if (variable.Address is StackAddressValue) { context.EmitInstruction(new IRStoreRegisterPlusImmediate() { From = "eax", To = "bp", Offset = new ImmediateValue(variable.Address.Value), OperandSize = assignmentExpressionType.GetSize() }); } else { context.EmitInstruction(new IRStoreImmediate() { From = "eax", To = variable.Address, OperandSize = assignmentExpressionType.GetSize() }); } } } } }
public void Emit(CompilationContext context) { context.EmitComment(";Assignment"); var leftSideExpressionType = ((IHasType)Tokens[0]).GetExpressionType(context); var rightSideExpressionType = ((IHasType)Tokens[2]).GetExpressionType(context); if (leftSideExpressionType.IsArray) { throw new TypeMismatchException(new ExpressionType() { IsArray = true, BaseType = leftSideExpressionType.BaseType, ArrayLength = leftSideExpressionType.ArrayLength }, rightSideExpressionType); } if (leftSideExpressionType.GetSize() == 0) { throw new VoidAssignmentException("to"); } else if (rightSideExpressionType.GetSize() == 0) { throw new VoidAssignmentException("from"); } TypeChecking.CheckExpressionTypesMatch(leftSideExpressionType, rightSideExpressionType); //Special case for assignment of string literal to byte array: // Don't emit string constant normally (which would add it as a string constant in the data section), // instead copy the string to the memory occupied by tge byte array itself if (rightSideExpressionType.BaseType is StringLiteralTypeDef && leftSideExpressionType.IsArray) { string stringLiteral = ((StringLiteralTypeDef)rightSideExpressionType.BaseType).Value; if (rightSideExpressionType.ArrayLength > leftSideExpressionType.ArrayLength) { throw new Exception("The string '" + stringLiteral + "' is too large to be assigned by value to the left hand expression."); } //Put start address to copy string value to into eax ((IHasAddress)Tokens[0]).PushAddress(context); context.EmitInstruction(new IRPop() { To = "eax" }); for (int i = 0; i < leftSideExpressionType.ArrayLength; i++) { byte charValue = i < stringLiteral.Length ? (byte)stringLiteral[i] : (byte)0; //Put char value into ebx context.EmitInstruction(new IRMoveImmediate() { To = "ebx", Value = new ImmediateValue(charValue), OperandSize = 1 }); //Store char value into address [eax + char offset] context.EmitInstruction(new IRStoreRegisterPlusImmediate() { To = "eax", Offset = new ImmediateValue(i), From = "ebx", OperandSize = 1 }); } } else { //right hand side value -> stack ((ICodeEmitter)Tokens[2]).Emit(context); if (rightSideExpressionType.GetSize() > 4) { //[sp] -> [destination] //Dest address -> eax ((IHasAddress)Tokens[0]).PushAddress(context); context.EmitInstruction(new IRPop() { To = "eax" }); //sp -= size of value context.EmitInstruction(new IRMoveImmediate() { To = "ebx", Value = new ImmediateValue(rightSideExpressionType.GetSize()) }); context.EmitInstruction(new IRSub() { Left = "sp", Right = "ebx", To = "sp" }); context.EmitInstruction(new IRMemCopy() { From = "sp", To = "eax", Length = new ImmediateValue(rightSideExpressionType.GetSize()) }); } else { //store ebx -> [destination] ((IHasAddress)Tokens[0]).PushAddress(context); context.EmitInstruction(new IRPop() { To = "eax" }); //Store assign value in ebx context.EmitInstruction(new IRPop() { To = "ebx" }); context.EmitInstruction(new IRStoreRegister() { From = "ebx", To = "eax", OperandSize = rightSideExpressionType.GetSize() }); //MB! } } }
public void Emit(CompilationContext context) { context.EmitComment(";Equality expression"); ((ICodeEmitter)Tokens[0]).Emit(context); var t1ExpressionType = ((IHasType)Tokens[0]).GetExpressionType(context); for (int i = 1; i < Tokens.Count; i += 2) { string op = ((DefaultLanguageTerminalToken)Tokens[i]).Value; ((ICodeEmitter)Tokens[i + 1]).Emit(context); var t2ExpressionType = ((IHasType)Tokens[i + 1]).GetExpressionType(context); TypeChecking.CheckExpressionTypesMatch(t1ExpressionType, t2ExpressionType); t1ExpressionType = t2ExpressionType; context.EmitInstruction(new IRPop() { To = "ebx" }); context.EmitInstruction(new IRPop() { To = "eax" }); context.EmitInstruction(new IRCompareRegister() { Left = "eax", Right = "ebx" }); var trueLabel = new LabelAddressValue(context.CreateNewLabel()); switch (op) { case "==": context.EmitInstruction(new IRJumpEQ() { Address = trueLabel }); break; case "!=": context.EmitInstruction(new IRJumpNE() { Address = trueLabel }); break; case ">": context.EmitInstruction(new IRJumpGT() { Address = trueLabel }); break; case "<": context.EmitInstruction(new IRJumpLT() { Address = trueLabel }); break; case ">=": context.EmitInstruction(new IRJumpGE() { Address = trueLabel }); break; case "<=": context.EmitInstruction(new IRJumpLE() { Address = trueLabel }); break; } var skipTrueLabel = new LabelAddressValue(context.CreateNewLabel()); context.EmitInstruction(new IRPushImmediate() { Value = new ImmediateValue(0) }); context.EmitInstruction(new IRJumpImmediate() { Address = skipTrueLabel }); context.EmitLabel(trueLabel.Value); context.EmitInstruction(new IRPushImmediate() { Value = new ImmediateValue(1) }); context.EmitLabel(skipTrueLabel.Value); } }