def binaryOperation(operation, reg1, reg2, outputReg, props): resultLines = [] standardComment = utils.formatComment('{} = {} {} {}'.format(outputReg, reg1, operation, reg2), props, 2) operationComment = utils.formatComment('{} {} {}'.format(reg1, '/' if operation == '%' else operation, reg2), props, 2) if reg1 == '0': reg1 = '$0' if reg2 == '0': reg2 = '$0' reg1 = utils.getRegister(reg1) reg2 = utils.getRegister(reg2) didChangeReg1 = False if (not operation in BIN_OPERATORS_IMM and utils.isIntOrChar(reg1)) or (utils.isIntOrChar(reg1) and utils.isIntOrChar(reg2)): tempReg = utils.getTempRegister(props['tempRegisters'], 0) resultLines.append(general.loadIntOrChar(reg1, tempReg, props)) reg1 = tempReg didChangeReg1 = True if not operation in BIN_OPERATORS_IMM and utils.isIntOrChar(reg2): tempReg = utils.getTempRegister(props['tempRegisters'], 1 if didChangeReg1 else 0) resultLines.append(general.loadIntOrChar(reg2, tempReg, props)) reg2 = tempReg # Using 2 registers if utils.isRegister(reg1) and utils.isRegister(reg2): if operation in BIN_RESULT_REGS: # Move from hi/lo resultLines.append('{} {}, {}{}'.format(BIN_OPERATORS[operation], reg1, reg2, operationComment)) resultLines.append(general.loadRegHiLo(BIN_RESULT_REGS[operation], outputReg, {**props, 'comment': standardComment})) else: resultLines.append('{} {}, {}, {}{}'.format(BIN_OPERATORS[operation], utils.getRegister(outputReg), reg1, reg2, standardComment)) else: # Immediate regNum, number = (reg1, utils.getIntOrChar(reg2)) if utils.isRegister(reg1) else (reg2, utils.getIntOrChar(reg1)) resultLines.append('{} {}, {}, {}{}'.format(BIN_OPERATORS_IMM[operation], utils.getRegister(outputReg), regNum, number, standardComment)) return { 'lines': resultLines, 'reg': outputReg }
def loadFromAddress(address, loadType, outputReg, props, outerInner=None): numLoadFuncs = { 'int': 'lw', 'short': 'lh', 'byte': 'lb', 'char': 'lb', 'number': 'lw', 'address': 'lw' } loadFunc = 'lw' if definitions.getSupertype(loadType) == 'address': loadType = 'address' if loadType in numLoadFuncs: loadFunc = numLoadFuncs[loadType] if utils.isRegister(address): raise utils.CompileException('Can\'t load directly from register') if outerInner == None: comment = utils.formatComment('{} = {}'.format(outputReg, address), props, 2) else: comment = utils.formatComment( '{} = {}[{}]'.format(outputReg, outerInner[0], outerInner[1]), props, 2) return '{} {}, {}{}'.format(loadFunc, utils.getRegister(outputReg), address, comment)
def parseAssignment(line, props): resultLines = [] lhs, rhs = [s.strip() for s in line.split('=')] if utils.isRegister(lhs): reg = lhs.split(' as ')[0].strip() if ' as ' in lhs else lhs parsedRHS = expressions.parseRHS(rhs, props, reg) resultLines += parsedRHS['lines'] else: inferredType = expressions.parseLHS(lhs, props)['type'] parsedRHS = expressions.parseRHS(rhs, props, 'any', inferredType) resultLines += parsedRHS['lines'] resultType = parsedRHS['type'] limitedProps = utils.propsWithTempRegExcl(props, [parsedRHS['reg']]) parsedLHS = expressions.parseLHS(lhs, limitedProps) resultLines += parsedLHS['lines'] resultType = definitions.updateType(resultType, parsedLHS['elemtype']) resultLines.append( general.storeToAddress(parsedLHS['address'], resultType, parsedRHS['reg'], props)) lastLine = resultLines[-1] if '#' in lastLine: lastLine = lastLine[:lastLine.index('#')].strip() resultLines[-1] = lastLine + utils.formatComment(line, props) return resultLines
def loadAsAddress(address, outputReg, props, outerInner=None): if utils.isRegister(address): raise utils.CompileException('Can\'t load register as address') if outerInner == None: comment = utils.formatComment( '{} = address of {}'.format(outputReg, address), props, 2) else: comment = utils.formatComment( '{} = address of {}[{}]'.format(outputReg, outerInner[0], outerInner[1]), props, 2) return '{} {}, {}{}'.format('la', utils.getRegister(outputReg), address, comment)
def storeToAddress(address, storeType, outputReg, props): if utils.isRegister(address): raise utils.CompileException('Can\'t store to register') numStoreFuncs = { 'int': 'sw', 'short': 'sh', 'byte': 'sb', 'char': 'sb', 'number': 'sw' } storeFunc = 'sw' if storeType in definitions.NUM_TYPES: storeFunc = numStoreFuncs[storeType] comment = utils.formatComment('{} = {}'.format(address, outputReg), props, 2) return '{} {}, {}{}'.format(storeFunc, utils.getRegister(outputReg), address, comment)
def parseRHS(expression, props, outputReg='any', inferredType='any'): # Convert the expression to postfix (RPN) notation splitExp = parser.infixToPostfix(parser.splitExpression(expression)) allTempRegs = props['tempRegisters'] resultLines = [] resultAddr = None # Parse the expression as a stack of operations equationStack = [] typeStack = [] asAddressStack = [] availableTempRegIdx = 0 for i, item in enumerate(splitExp): val1, val2 = item, None val1Type, val2Type = inferredType, inferredType val1AsAddress, val2AsAddress = False, False val1Addr, val2Addr = None, None # Haven't optimized address addition yet reg1, reg2 = None, None limitedProps = utils.propsWithTempRegOffset(props, availableTempRegIdx) finalIteration = i == len(splitExp) - 1 or ( i == len(splitExp) - 3 and (splitExp[-1] == 'as' or splitExp[-1] == 'addressof')) targetReg = utils.getTempRegister(limitedProps['tempRegisters'], 0) if finalIteration: if utils.isSysFunc(item) and outputReg == 'any': targetReg = '$v0' elif outputReg == 'any': targetReg = utils.getTempRegister(allTempRegs, 0) else: targetReg = outputReg prevTempReg = None if availableTempRegIdx < 1 else utils.getTempRegister( allTempRegs, availableTempRegIdx - 1) prevPrevTempReg = None if availableTempRegIdx < 2 else utils.getTempRegister( allTempRegs, availableTempRegIdx - 2) if item in operations.BIN_OPERATORS or item == 'as': val1, val2 = equationStack[-2], equationStack[-1] val1Type, val2Type = typeStack[-2], typeStack[-1] val1AsAddress, val2AsAddress = asAddressStack[-2], asAddressStack[ -1] elif item in operations.UN_OPERATORS or item == 'addressof': val1, val1Type, val1AsAddress = equationStack[-1], typeStack[ -1], asAddressStack[-1] # Special operators if item == 'as': equationStack.pop() asAddressStack.pop() typeStack.pop() typeStack.pop() typeStack.append(val2) val1, val1Type, val1AsAddress = equationStack[-1], typeStack[ -1], asAddressStack[-1] elif item == 'addressof': asAddressStack.pop() asAddressStack.append(True) val1, val1Type, val1AsAddress = equationStack[-1], typeStack[ -1], asAddressStack[-1] else: # Get first register if item in operations.BIN_OPERATORS or item in operations.UN_OPERATORS: if utils.isIntOrChar(val1): reg1 = val1 else: if utils.isRegister(val1): reg1 = val1 else: reg1 = targetReg parsed = parseSingleRHS(val1, limitedProps, reg1, val1Type, val1AsAddress) resultLines += parsed['lines'] val1Type, val1Addr = parsed['type'], parsed['address'] if not utils.isRegister(val1): limitedProps = utils.propsWithTempRegOffset( limitedProps, 1) # Get Second register if item in operations.BIN_OPERATORS: if utils.isIntOrChar(val2): reg2 = val2 else: if utils.isRegister(val2): reg2 = val2 elif reg1 == targetReg: reg2 = utils.getTempRegister( limitedProps['tempRegisters'], 0) else: reg2 = targetReg parsed = parseSingleRHS(val2, limitedProps, reg2, val2Type, val2AsAddress) resultLines += parsed['lines'] val2Type, val2Addr = parsed['type'], parsed['address'] if not utils.isRegister(val2): limitedProps = utils.propsWithTempRegOffset( limitedProps, 1) # Do binary operation if item in operations.BIN_OPERATORS: # Try to re-use/free-up temporary registers where possible equationReg = targetReg if utils.isTempRegister(equationReg, allTempRegs): if val1 == prevPrevTempReg: equationReg = val1 elif val2 == prevTempReg: equationReg = val2 else: availableTempRegIdx += 1 if val1 == prevPrevTempReg and val2 == prevTempReg: # Suggested Optimization: Even though it frees up a register, it doesn't # keep track of the value inside it. This could potentially prevent # the same value from being re-loaded. availableTempRegIdx -= 1 # Parse the operation parsedOperation = operations.binaryOperation( item, reg1, reg2, equationReg, limitedProps) resultLines += parsedOperation['lines'] equationStack.pop() equationStack.pop() typeStack.pop() typeStack.pop() asAddressStack.pop() asAddressStack.pop() equationStack.append(parsedOperation['reg']) typeStack.append(definitions.mergeType(val1Type, val2Type)) asAddressStack.append(False) # Do unary operation elif item in operations.UN_OPERATORS: # Try to re-use/free-up temporary registers where possible equationReg = targetReg if utils.isTempRegister(equationReg, allTempRegs): if val1 == prevTempReg: equationReg = val1 else: availableTempRegIdx += 1 # Parse the operation parsedOperation = operations.unaryOperation( item, reg1, equationReg, limitedProps) resultLines += parsedOperation['lines'] equationStack.pop() typeStack.pop() asAddressStack.pop() equationStack.append(parsedOperation['reg']) typeStack.append(val1Type) asAddressStack.append(False) # Handle expressions without operators elif i == len(splitExp) - 1: if outputReg == 'any': if val1 == '0': val1 = '$0' if utils.isRegister(val1): targetReg = val1 parsed = parseSingleRHS(val1, limitedProps, targetReg, val1Type, val1AsAddress) resultLines += parsed['lines'] val1Type, val1Addr = parsed['type'], parsed['address'] resultAddr = val1Addr equationStack.append(targetReg) typeStack.append(val1Type) asAddressStack.append(False) # Add to stack elif item != 'as' and item != 'of': equationStack.append(item) if utils.isInt(item): typeStack.append('number') elif utils.isChar(item): typeStack.append('char') else: typeStack.append('any') asAddressStack.append(False) return { 'lines': resultLines, 'type': typeStack[-1], 'reg': equationStack[-1], 'address': resultAddr }
def parseSingleRHS(expression, props, outputReg, inferredType='any', loadAsAddress=False): resultLines = [] resultAddr = None resultType = inferredType if loadAsAddress and (utils.isRegister(expression) or utils.isSysFunc(expression)): raise utils.CompileException('Can\'t get address of register') # System functions if utils.isSysFunc(expression): resultLines += functions.parseSysFunc(expression, props, True) resultType = definitions.updateType(resultType, 'number') if outputReg != '$v0': resultLines.append(general.loadRegister('$v0', outputReg, props)) # Array elif addresses.isArray(expression): parsedAddr = addresses.parseArray(expression, props, outputReg) outerInner = (parsedAddr['outer'], parsedAddr['inner']) resultLines += parsedAddr['lines'] if loadAsAddress: resultType = definitions.updateType('address', resultType) if parsedAddr['reg'] != outputReg: resultLines.append( general.loadAsAddress(parsedAddr['address'], outputReg, props, outerInner)) if parsedAddr['reg'] == None: resultAddr = parsedAddr['address'] else: loadType = definitions.updateType(resultType, parsedAddr['elemtype']) resultLines.append( general.loadFromAddress(parsedAddr['address'], loadType, outputReg, props, outerInner)) resultType = definitions.updateType(loadType, resultType) # Register elif utils.isRegister(expression): if general.isRegHiLo(expression): resultLines.append( general.loadRegHiLo(expression, outputReg, props)) elif utils.getRegister(expression) != utils.getRegister(outputReg): resultLines.append( general.loadRegister(expression, outputReg, props)) # Variable elif expression in props['variables']: varName, varType = props['variables'][expression]['name'], props[ 'variables'][expression]['type'] if definitions.getSupertype(varType) == 'address' or loadAsAddress: resultLines.append(general.loadAsAddress(varName, outputReg, props)) resultType = definitions.updateType(varType, resultType) resultAddr = varName else: resultLines.append( general.loadFromAddress(varName, varType, outputReg, props)) resultType = definitions.updateType(varType, resultType) # Integer or char literal elif utils.isIntOrChar(expression): resultType = definitions.updateType( resultType, 'char' if utils.isChar(expression) else 'number') resultLines.append(general.loadIntOrChar(expression, outputReg, props)) # Address elif addresses.isAddress(expression, props): resultType = definitions.updateType('address', resultType) if definitions.getSupertype(resultType) == 'address': resultLines.append( general.loadAsAddress(expression, outputReg, props)) else: resultLines.append( general.loadFromAddress(expression, resultType, outputReg, props)) resultAddr = expression return { 'lines': resultLines, 'type': resultType, 'reg': outputReg, 'address': resultAddr }
def parseArray(expression, props, addressReg='any'): resultLines = [] resultReg = None resultType = 'address' elemType = 'any' resultAddress = 'none' outer, inner = None, None outerAddr = expression[:expression.index('[')] innerExpr = expression[expression.index('[') + 1:-1].strip() if outerAddr in props['variables'] and 'elemtype' in props['variables'][ outerAddr]: resultType = definitions.updateType( props['variables'][outerAddr]['type'], 'address') if 'elemtype' in props['variables'][outerAddr]: elemType = props['variables'][outerAddr]['elemtype'] # Optimize offset literals if utils.isIntOrChar(innerExpr): value = utils.getIntOrChar(innerExpr) if utils.isRegister(outerAddr): if value == 0: resultAddress = '({})'.format(utils.getRegister(outerAddr)) else: resultAddress = '{}({})'.format(value, utils.getRegister(outerAddr)) else: if value == 0: resultAddress = outerAddr else: resultAddress = '{}+{}'.format(outerAddr, value) outer, inner = outerAddr, value else: # Parse inner expression exprReg = None if utils.isRegister(innerExpr): exprReg = innerExpr else: parsedExpr = expressions.parseRHS(innerExpr, props, 'any', 'number') resultLines += parsedExpr['lines'] exprReg = parsedExpr['reg'] limitedProps = utils.propsWithTempRegExcl(props, [exprReg]) resultReg = addressReg if resultReg is 'any' or resultReg == exprReg: resultReg = utils.getTempRegister(limitedProps['tempRegisters'], 0) newAddress = outerAddr if utils.isRegister(outerAddr): newAddress = '({})'.format(utils.getRegister(outerAddr)) resultLines.append( general.loadAsAddress(newAddress, resultReg, props, (outerAddr, '0'))) addOperation = operations.binaryOperation('+', resultReg, exprReg, resultReg, limitedProps) resultLines += addOperation['lines'] resultAddress = '({})'.format(utils.getRegister(resultReg)) outer, inner = resultReg, '0' return { 'lines': resultLines, 'type': resultType, 'elemtype': elemType, 'reg': resultReg, 'address': resultAddress, 'outer': outer, 'inner': inner }
def parseGotoConditional(line, props): resultLines = [] gotoFunc = line.split()[0] branchOperators = ['==', '!=', '<', '<=', '>', '>='] branchFuncs = { '==': 'beq', '!=': 'bne', '<': 'blt', '<=': 'ble', '>': 'bgt', '>=': 'bge' } branchFuncsWith0 = {'<': 'bltz', '<=': 'blez', '>': 'bgtz', '>=': 'bgez'} branchFuncsWith0Link = {'<': 'bltzal', '>=': 'bgezal'} condition = line.split(' if ')[1].strip() location = line.split(' if ')[0][len(gotoFunc):].strip() address = expressions.parseRHS(location, props, 'any', 'address')['address'] if address == None: raise utils.CompileException('Direct label is required') conditionLeft, conditionRight, conditionOperator = '', '', None splitCondition = parser.splitExpression(condition) rpnCondition = parser.infixToPostfix(splitCondition) if rpnCondition[-1] not in branchOperators: conditionLeft = '({})'.format(condition) conditionRight = '0' conditionOperator = '>' else: splitIdxCondition = [(item, idx) for idx, item in enumerate(splitCondition)] rpnIdxCondition = parser.infixToPostfix(splitIdxCondition, lambda item: item[0]) conditionOperator = rpnIdxCondition[-1][0] operatorIdx = rpnIdxCondition[-1][1] conditionLeft = ' '.join(splitCondition[:operatorIdx]) conditionRight = ' '.join(splitCondition[operatorIdx + 1:]) branchFunc = branchFuncs[conditionOperator] singleParam = False reg1, reg2 = 'any', 'any' if utils.isRegister(conditionLeft): reg1 = utils.getRegister(conditionLeft) else: parsed = expressions.parseRHS(conditionLeft, props, 'any', 'number') resultLines += parsed['lines'] reg1 = parsed['reg'] limitedProps = utils.propsWithTempRegExcl(props, [reg1]) if gotoFunc == 'gotolink': if conditionRight == '0' and conditionOperator in branchFuncsWith0Link: branchFunc = branchFuncsWith0Link[conditionOperator] singleParam = True else: raise utils.CompileException( 'Conditional gotolink only supports < 0 and >= 0 conditions') elif conditionRight == '0' and conditionOperator in branchFuncsWith0: branchFunc = branchFuncsWith0[conditionOperator] singleParam = True elif utils.isRegister(conditionRight): reg2 = utils.getRegister(conditionRight) else: parsed = expressions.parseRHS(conditionRight, limitedProps, 'any', 'number') resultLines += parsed['lines'] reg2 = parsed['reg'] comment = utils.formatComment(line, props) if singleParam: resultLines.append('{} {}, {}{}'.format(branchFunc, reg1, address, comment)) else: resultLines.append('{} {}, {}, {}{}'.format(branchFunc, reg1, reg2, address, comment)) return resultLines