///<summary> /// Returns a list of assembly instructions that evaluate a given expression, /// returning the result in a specified <c>dest</c>ination <see>Variable</see>. ///</summary> private List <string> EvaluateExpression(Expression expression, Variable dest, ref Dictionary <Variable, int> stackMap, int stackClobber) { /*//TODO: Refactor `stackClobber` * We need a better way of handling ad-hoc expands and reductions of the current * stack frame. Currently, I use the `stackClobber` variable inconsistently in order * to signify that there's a manually applied change from the `stackMap`, and to let * this method know that when reading or writing to stack addresses that aren't a part * of this evaluation, we need to offset by the `stackClobber`. However, this is dirty * and hacky and inconsistent. * * //* A much better approach would be to maintain the `stackMap` as an absolute invariant, * extending its `.Add()` and `.Remove()` methods to update the stack positions of all the * other variables. This would eliminate a lot of inconsistencies, hacky patch jobs, and * allow for much easier further expansion to more complicated operations involving the stack. */ List <string> result = new List <string>(); result.Add(" # [EvaluateExpression " + expression.ToString() + "]"); if (stackMap.ContainsKey(dest)) { // Destination is already on the stack. int destOffset = stackMap[dest]; // Need to back up $r1, set $r1 to the value, then // sw $r1 --> stack[offset], then restore $r1 result.Add(" addi $r6, $r6, -1"); // Expand the stack result.Add(" sw $r1, 0($r6)"); // Backup $r1 to stack result.Add(" xor $r1, $r1, $r1"); // Zero out $r1 if (expression is LiteralConstant) { int newVal = (expression as LiteralConstant).value; while (newVal > 31) { result.Add( " addi $r1, $r1, 31" ); newVal -= 31; } result.Add( " addi $r1, $r1, " + newVal.ToString() // Set $r1 = literal value ); } else if (expression is Variable) { // This is a simple one. Simply get the variable's value and put it in $r1 int sourceOffset = stackMap[(expression as Variable)]; result.Add(" lw $r1, " + (sourceOffset + stackClobber + 1) + "($r6)"); } else if (expression is SingleParamOperation) { //TODO: Single parameter operators. throw new NotImplementedException("Single param operators are not yet supported."); } else if (expression is TwoParamOperation) { // Get an ID string unique = UniqueIdentifier.NextStr(); // Evaluate the operands Variable leftOperand = new Variable() { name = "__temp_leftop_" + unique, type = Variable.VarType.Int }; Variable rightOperand = new Variable() { name = "__temp_rightop_" + unique, type = Variable.VarType.Int }; result.Add(" addi $r6, $r6, -2"); stackMap.Add(leftOperand, 0); stackMap.Add(rightOperand, 1); result.AddRange(EvaluateExpression( (expression as TwoParamOperation).operands[0], leftOperand, ref stackMap, stackClobber + 2 )); result.AddRange(EvaluateExpression( (expression as TwoParamOperation).operands[1], rightOperand, ref stackMap, stackClobber + 2 )); result.Add(" # " + (expression as TwoParamOperation).ToString()); switch ((expression as TwoParamOperation).operationType) { case Operation.OperationType.TestEq: { // Load both evaluated operands into registers result.Add(" lw $r2, " + (stackMap[leftOperand]) + "($r6)"); result.Add(" lw $r3, " + (stackMap[rightOperand]) + "($r6)"); // Get a unique ID string ifUnique = UniqueIdentifier.NextStr(); // Assume they're equal result.Add(" addi $r1, $r0, 1"); // If they are, done! result.Add(" beq $r2, $r3, __beq_" + ifUnique); // Otherwise, $r1 <- 0 result.Add(" addi $r1, $r0, 0"); // Done result.Add(" __beq_" + ifUnique + ":"); } break; //TODO: Other comparisons // case Operation.OperationType.TestGt: { // } break; // case Operation.OperationType.TestLt: { // } break; // case Operation.OperationType.TestGeq: { // } break; // case Operation.OperationType.TestLeq: { // } break; case Operation.OperationType.Addition: { // Load both evaluated operands into registers result.Add(" lw $r2, " + (stackMap[leftOperand]) + "($r6)"); result.Add(" lw $r3, " + (stackMap[rightOperand]) + "($r6)"); // Add them into another register result.Add(" add $r1, $r2, $r3"); } break; case Operation.OperationType.Subtraction: { // Load both evaluated operands into registers result.Add(" lw $r2, " + (stackMap[leftOperand]) + "($r6)"); result.Add(" lw $r3, " + (stackMap[rightOperand]) + "($r6)"); // Subtract them into another register result.Add(" sub $r1, $r2, $r3"); } break; case Operation.OperationType.Xor: { // Load both evaluated operands into registers result.Add(" lw $r2, " + (stackMap[leftOperand]) + "($r6)"); result.Add(" lw $r3, " + (stackMap[rightOperand]) + "($r6)"); // XOR them into another register result.Add(" xor $r1, $r2, $r3"); } break; //TODO: Shift operators // case Operation.OperationType.ShiftLeft: { // } break; // case Operation.OperationType.ShiftRight: { // } break; default: { throw new InvalidOperationException( "Invalid operation: " + (expression as TwoParamOperation).operationType.ToString() ); } } // Reset the stack stackMap.Remove(leftOperand); stackMap.Remove(rightOperand); result.Add(" addi $r6, $r6, 2"); } else { throw new NotImplementedException("Only supports evaluation of literal constants."); } result.Add( " sw $r1, " + (destOffset + stackClobber).ToString() // Write to stack[dest] + "($r6)" // Offset of destOffset + 1 because we backed up $r1 to stack ); result.Add(" lw $r1, 0($r6)"); // Restore $r1 from stack result.Add(" addi $r6, $r6, 1"); // Restore the stack return(result); } else { // Destination doesn't exist on the stack. throw new ArgumentOutOfRangeException("Destination: " + dest.ToString() + " not on stack."); } }
private void EmitActionCode(ref List <string> result, Action action, Function function, ref Dictionary <Variable, int> stackMap, ref int stackSize) { // Emit comment with action ToString header result.Add("\n # " + action.ToString()); if (action is InlineAssembly) { // For an inline assembly command, we just need to output all of the lines // directly to the .text section foreach (string line in (action as InlineAssembly).code) { result.Add(" " + line); } } else if (action is ReturnInstruction) { if (!((action as ReturnInstruction).returnValue is null)) { // Need to evaluate the returnValue and place it in $r5 // $r5 isn't on the stack, so we have to define a new temp variable Variable tempResult = new Variable() { name = "__temp_expr", type = Variable.VarType.Int }; result.Add(" addi $r6, $r6, -1"); stackMap.Add(tempResult, 0); List <string> steps = EvaluateExpression( (action as ReturnInstruction).returnValue, tempResult, ref stackMap, 1 ); result.AddRange(steps); // Result is now in tempResult. Need to put it in $r5 and then jr $ra result.Add(" xor $r5, $r5, $r5"); result.Add(" lw $r5, " + stackMap[tempResult] + "($r6)"); // Restore the dirty stack stackMap.Remove(tempResult); result.Add(" addi $r6, $r6, 1"); } result.AddRange(GenerateReturn(stackSize)); } else if (action is FunctionCall) { // First, we need to evaluate each argument onto the stack // How many args? int numArgs = (action as FunctionCall).arguments.Count; if (numArgs > 4) { throw new ArgumentException( "Can't have more than 4 arguments (" + (action as FunctionCall).function.name + ")" ); } // Evaluate all arguments --> for (int argNum = 1; argNum <= numArgs; argNum++) { Variable tempResult = new Variable() { name = "__temp_expr_" + UniqueIdentifier.NextStr(), type = Variable.VarType.Int }; result.Add(" addi $r6, $r6, -1"); stackMap.Add(tempResult, 0); List <string> steps = EvaluateExpression( (action as FunctionCall).arguments[argNum - 1], tempResult, ref stackMap, 1 ); result.AddRange(steps); // Result is now in tempResult. Need to put it in correct arg register result.Add( " lw $r" + argNum.ToString() + ", " + stackMap[tempResult] + "($r6)" ); // Restore the dirty stack stackMap.Remove(tempResult); result.Add(" addi $r6, $r6, 1"); } // Now, do the function call string calleeName = (action as FunctionCall).function.name; result.Add(" jal " + calleeName); } else if (action is LocalVarDeclaration) { // The variable already exists! // Only need to do something if we are initializing it with a value. Variable target = (action as LocalVarDeclaration).variable; Expression newVal = (action as LocalVarDeclaration).initValue; // Need to evaluate the new value and place it in the target List <string> steps = EvaluateExpression( newVal, target, ref stackMap, 1 ); result.AddRange(steps); } else if (action is VarAssignment) { // The variable already exists! // Only need to do something if we are initializing it with a value. Variable target = (action as VarAssignment).variable; Expression newVal = (action as VarAssignment).newValue; // Need to evaluate the new value and place it in the target List <string> steps = EvaluateExpression( newVal, target, ref stackMap, 0 ); result.AddRange(steps); } else if (action is If) { // First, need to evaluate the if condition Expression ifCond = (action as If).condition; Variable tempResult = new Variable() { name = "__temp_expr_" + UniqueIdentifier.NextStr(), type = Variable.VarType.Int }; result.Add(" addi $r6, $r6, -2"); stackMap.Add(tempResult, 0); List <string> steps = EvaluateExpression( ifCond, tempResult, ref stackMap, 2 ); result.AddRange(steps); // Result is now in tempResult //result.Add(" sw $r1, 1($r6)"); result.Add(" lw $r1, " + (stackMap[tempResult] + 1) + "($r6)"); // Result of condition is now in $r1 // Can be either 1 or 0. If it's 1 --> keep going, but if it's 0 --> skip body. string skipLabel = "__if_skip_" + UniqueIdentifier.NextStr(); result.Add(" addi $r6, $r6, 1"); result.Add(" beq $r1, $r0, " + skipLabel); // If cond = 0 --> skip //result.Add(" lw $r1, 1($r6)"); // Body of the IF block foreach (Action ifAction in (action as If).ifActions) { EmitActionCode(ref result, ifAction, function, ref stackMap, ref stackSize); } result.Add(" " + skipLabel + ":"); // Restore the dirty stack stackMap.Remove(tempResult); result.Add(" addi $r6, $r6, 1"); } }