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" }); } }