def decl_var(self, t): type = t.children[0] name = Location(t.children[1]) lv = self._currentFunction.localVars if name in lv: raise SemanticError(Position.fromAny(t), f"Local variable {name} redifinition") lv[name] = type
def __init__(self, position, type, indirLevel, src, aligned): if indirLevel < 0: raise SemanticError(position, "Cannot take address") self._position = position self._type = type self._level = indirLevel self._src = Location(src) self._isAligned = aligned
def error(cls, error_code: ErrorCode, token: Token, var_name: str) -> NoReturn: raise SemanticError( error_code=error_code, token=token, message= f"{error_code.value} \n \t{token} \n \tIdentifier: '{var_name}'", )
def arrow(self, t): obj = t.children[0] fields = t.children[1:] if isinstance(obj, Value) and obj.getType().isPointer( ) and obj.getIndirLevel() == 0: try: offset, type = getField(obj.getType().deref(), fields) except ValueError as e: raise SemanticError(Position.fromAny(t), str(e)) except AttributeError as e: raise SemanticError(Position.fromAny(t), f"{obj} is not a struct") return Value.withOffset(Position.fromAny(t), type, 1 + type.getIndirectionOffset(), obj.getSource(), False, offset) else: return t
def function_definition(self, t): decl, body = t.children fn = self._addFunction(decl, True) if fn.isImported: raise SemanticError(Position.fromAny(decl), "Cannot define an imported function") self._currentFunction = fn self.visit(body) self._currentFunction = None
def genBNot(resultLoc, srcLoc): if resultLoc.getType().isUnknown(): resultLoc = resultLoc.removeUnknown(srcLoc.getType()) if resultLoc.getType() != srcLoc.getType(): raise SemanticError( srcLoc.getPosition(), "Incompatible types: {} and {}".format(resultLoc.getType(), srcLoc.getType())) assert (resultLoc.getIndirLevel() == 1) assert (srcLoc.getIndirLevel() == 1 or srcLoc.getIndirLevel() == 0) t = srcLoc.getType() result = '; {} = ~{}\n'.format(resultLoc, srcLoc) if srcLoc.getIndirLevel() == 0: # constant c = srcLoc.getSource() c = ~c return Value(srcLoc.getPosition(), t, 0, c, True), result # var s = srcLoc.getSource() rs = resultLoc.getSource() if t.getSize() == 1: result += loadByte('a', srcLoc, 0) result += 'not a\n' return Value.register(srcLoc.getPosition(), t), result else: # size > 1 for offset in range(0, t.getSize(), 2): rest = t.getSize() - offset result += f''' ldi pl, lo({s} + {offset}) ldi ph, hi({s} + {offset}) ld b ''' if rest > 1: result += incP(srcLoc.isAligned()) result += 'ld a\n' result += f''' ldi pl, lo({rs} + {offset}) ldi ph, hi({rs} + {offset}) not b st b ''' if rest > 1: if resultLoc.isAligned: result += ''' inc pl not a st a ''' else: result += ''' mov b, a inc pl mov a, 0 adc ph, a not b st b ''' return resultLoc, result
def type_error(cls, *args, token: Token) -> NoReturn: formatted_variable_types = (" and ".join(args) if len(args) == 2 else ", ".join(args)) raise SemanticError( error_code=ErrorCode.TYPE_ERROR, token=token, message= f"{ErrorCode.TYPE_ERROR.value}: {formatted_variable_types}\n \t{token}", )
def takeAddress(self, newPosition=None): if self._level > 0: return Value( self._position if newPosition is None else newPosition, PtrType(self._type), self._level - 1, self._src, True) else: raise SemanticError( self._position, "Cannot get address of this: {}".format(str(self)))
def genDeref(resultLoc, srcLoc, offset=0): if resultLoc.getType().isUnknown(): resultLoc = resultLoc.removeUnknown(srcLoc.getType().deref()) if srcLoc.getType().deref() != resultLoc.getType() and not srcLoc.getType( ).deref().isStruct(): raise SemanticError( srcLoc.getPosition(), "Incompatible types for deref: {} and {}".format( srcLoc.getType().deref(), resultLoc.getType())) assert (srcLoc.getIndirLevel() <= 1) t = resultLoc.getType() if srcLoc.getIndirLevel() == 0: return Value.withOffset(srcLoc.getPosition(), resultLoc.getType(), 1, srcLoc.getSource(), True, offset), "" result = '; {} = deref {} + {}\n'.format(resultLoc, srcLoc, offset) result += '; result is {}aligned, srcLoc is {}aligned'.format( "" if resultLoc.isAligned() else "not ", "" if srcLoc.isAligned() else "not ") rs = resultLoc.getSource() if t.getSize() == 1: result += loadP(srcLoc, offset) result += 'ld a\n' return Value.register(srcLoc.getPosition(), t), result else: # t.getSize() > 1 for byteOffset in reversed(range(0, t.getSize(), 2)): rest = min(2, t.getSize() - byteOffset) result += loadP(srcLoc, byteOffset + offset) result += 'ld b\n' if rest > 1: result += ''' mov a, 0 inc pl adc ph, a ld a ''' result += f''' ldi pl, lo({rs} + {byteOffset}) ldi ph, hi({rs} + {byteOffset}) st b ''' if rest > 1: if resultLoc.isAligned(): result += ''' inc pl st a ''' else: result += f''' ldi pl, lo({rs} + {byteOffset + 1}) ldi ph, hi({rs} + {byteOffset + 1}) st a ''' return resultLoc, result
def function_definition(self, tree): decl, body = tree.children _, _, name, _ = decl.children self.visit(decl) try: self._enterFunction(str(name)) except ValueError as e: raise SemanticError(Position.fromAny(tree), str(e)) self.visit(body) self._leaveFunction()
def addr(self, t): val = t.children[0] if isinstance(val, Value): return val.takeAddress(Position.fromAny(t)) elif val.data == "member_access": return Tree("member_address", val.children, t.meta) elif val.data == "arrow": return Tree("p_arrow", val.children, t.meta) else: raise SemanticError(Position.fromAny(t), "Cannot get address")
def genSub(resultLoc, src1Loc, src2Loc): if resultLoc.getType().isUnknown(): resultLoc = resultLoc.removeUnknown(src1Loc.getType()) if resultLoc.getType() != src1Loc.getType(): raise SemanticError(resultLoc.getPosition(), "Incompatible result and source types: {} and {}".format(resultLoc.getType(), src1Loc.getType())) if src1Loc.getType().isPointer(): return _genSubPtr(resultLoc, src1Loc, src2Loc) else: return _genIntBinary(resultLoc, src1Loc, src2Loc, "sub", "sbb", "({}) - ({})", operator.sub, False)
def member_address(self, t): obj = t.children[0] fields = t.children[1:] if isinstance(obj, Value): try: offset, type = getField(obj.getType(), fields) except ValueError as e: raise SemanticError(Position.fromAny(t), str(e)) except AttributeError as e: raise SemanticError(Position.fromAny(t), f"{obj} is not a struct") if obj.getIndirLevel() != 1: raise RuntimeError("WTF is that") return Value.withOffset(Position.fromAny(t), PtrType(type), 0 + type.getIndirectionOffset(), obj.getSource(), True, offset) elif obj.data == 'deref': return Tree("p_arrow", [obj.children[0]] + fields, t.meta) else: raise RuntimeError("Unhandled member_access case")
def struct_declaration(self, t): name = t.children[0] fields = [] for c in t.children[1:]: ftype = c.children[0] fname = c.children[1] fields += [(fname, ftype)] if name in self._dict: raise SemanticError(Position.fromAny(t), f"struct {name} redefinition") self._dict[name] = fields raise Discard
def type_cast(self, t): type, v = t.children from value import Value if isinstance(v, Value) and v.getIndirLevel() == 0: s = v.getSource() if type.getSize() > 2: raise SemanticError(Position.fromAny(t), f"{str(type)} has size > 2, cannot cast") if v.getType().getSize() > 2: raise SemanticError( Position.fromAny(t), f"{str(v.getType())} has size > 2, cannot cast") if type.getSize() > v.getType().getSize(): # widening cast s = s.widen(v.getType().getSign()) elif type.getSize() < v.getType().getSize(): # narrowing cast s = s.lo() return Value(Position.fromAny(t), type, 0, s, True) else: return t
def array_field_declaration(self, t): type, name, size = t.children from value import Value if isinstance(size, Value): if size.getSource().isNumber(): if size.getIndirLevel() == 0 and int(size.getSource()) > 0: return Tree("field_declaration", [ArrayType(type, size.getSource()), name], t.meta) raise SemanticError( Position.fromAny(t), "Array size must be a positive constant expression")
def genCmp(resultLoc, src1Loc, src2Loc, op, labelProvider): assert (op in {"gt", "ge", "lt", "le"}) resultLoc = resultLoc.withType(BoolType()) assert (resultLoc.getIndirLevel() == 1) if src1Loc.getType() != src2Loc.getType(): raise SemanticError( src1Loc.getPosition(), "Incompatible types: {} and {}".format(src1Loc.getType(), src2Loc.getType())) t = src1Loc.getType() if t.getSign(): return _genCmpSigned(resultLoc, src1Loc, src2Loc, op, labelProvider) else: return _genCmpUnsigned(resultLoc, src1Loc, src2Loc, op, labelProvider)
def decl_array(self, t): type = t.children[0] size = t.children[2] from value import Value if isinstance(size, Value): if size.getSource().isNumber(): if size.getIndirLevel() == 0 and int(size.getSource()) > 0: return Tree( "decl_var", [ArrayType(type, size.getSource()), t.children[1]], t.meta) raise SemanticError( Position.fromAny(t), "Array size must be a positive constant expression")
def genShift(resultLoc, src1Loc, src2Loc, op, labelProvider): assert (resultLoc.getIndirLevel() == 1) t = src1Loc.getType() if not t.isInteger(): raise SemanticError(src1Loc.getPosition(), "Can only shift integers") if not src2Loc.getType().isInteger() or src2Loc.getType().getSign(): raise SemanticError(src2Loc.getPosition(), "Can only shift by unsigned integers") resultLoc = resultLoc.withType(t) l1 = src1Loc.getIndirLevel() l2 = src2Loc.getIndirLevel() assert (l1 == 0 or l1 == 1) assert (l2 == 0 or l2 == 1) if l1 == 0 and l2 == 0: raise NatrixNotImplementedError(Position.fromAny(resultLoc), "Stop doing shit with pointers!") if l2 == 0: c = src2Loc.getSource() if c.isNumber(): if op == 'shl': return genSHLVarByConst(resultLoc, src1Loc, int(c)) elif op == 'shr': if t.getSign(): return _genSARVarByConst(resultLoc, src1Loc, int(c)) else: return _genSHRVarByConst(resultLoc, src1Loc, int(c)) else: raise NatrixNotImplementedError(Position.fromAny(resultLoc), "Stop doing shit with pointers!") else: if op == 'shl': return _genSHLByVar(resultLoc, src1Loc, src2Loc, labelProvider) elif op == 'shr': if t.getSign(): return _genSARByVar(resultLoc, src1Loc, src2Loc, labelProvider) else: return _genSHRByVar(resultLoc, src1Loc, src2Loc, labelProvider)
def parse(input: str): """Takes in a string input and returns a graph""" var_definitions_map = OrderedDict() links = OrderedDict() lines = input.split('\n') for line_num, line in enumerate(lines): line = line.strip() if not line or line[0] == '#': continue declaration, dec_position, dec_err = parse_declaration(line, 0) if declaration is None: chain, chain_position, chain_err = parse_chain(line, 0) if chain is None: err = dec_err if dec_position > chain_position else chain_err err_col = max(dec_position, chain_position) raise SyntaxError(f'SYNTAX_ERROR<Line {line_num+1}, Col {err_col+1}>: {err}') else: nodes, link_types = chain node_vars = [] for i, node in enumerate(nodes): type = node['type'] var = node['value'] if type == 'string': # Create a variable var = str(string_hash(node['value'] + (node['enclosure'] or ''))) var_definitions_map[var] = node['value'] node_vars.append(f"{node['enclosure']}{var}") else: node_vars.append(f"{node['value'][1]}{node['value'][0]}") # Now nodes are ready, create links for (source, dest), link_type in zip(zip(node_vars, node_vars[1:]), link_types): if link_type == '<-': source, dest = dest, source links[source] = [*links.get(source, []), dest] else: name, val = declaration present = var_definitions_map.get(name) if present is not None: raise SemanticError(f"ERROR<Line {line_num + 1}>: variable '{name}' already defined") else: var_definitions_map[name] = val return to_graph(var_definitions_map, links)
def _genDMCommon(resultLoc, src1Loc, src2Loc): if src1Loc.getType() != src2Loc.getType(): raise SemanticError(src1Loc.getPosition() - src2Loc.getPosition(), "division types mismatch") t = src1Loc.getType() resultLoc = resultLoc.withType(t) assert (t.getSize() == 1 or t.getSize() == 2) l1 = src1Loc.getIndirLevel() l2 = src2Loc.getIndirLevel() isWord = t.getSize() == 2 result = '; {} = {} / {}\n'.format(resultLoc, src1Loc, src2Loc) if l1 == 0 and l2 == 0: raise NotImplementedError("doing shit with pointers?") if isWord: if l1 == 0: result += storeConstW(src1Loc.getSource(), "__cc_r_a", False) result += copyW(src2Loc.getSource(), "__cc_r_b", False, True) elif l2 == 0: s = src2Loc.getSource() result += copyW(src1Loc.getSource(), "__cc_r_a", False, True) result += storeConstW(src2Loc.getSource(), "__cc_r_b", False) else: result += copyW(src1Loc.getSource(), "__cc_r_a", False, True) result += copyW(src2Loc.getSource(), "__cc_r_b", False, True) else: result += loadByte('b', src1Loc, 0) result += loadByte('a', src2Loc, 0) result += ''' ldi pl, lo(__cc_r_a) ldi ph, hi(__cc_r_a) st b ldi pl, lo(__cc_r_b) st a ''' if isWord: if t.getSign(): result += call("__cc_div_word") else: result += call("__cc_udiv_word") else: if t.getSign(): result += call("__cc_div_byte") else: result += call("__cc_udiv_byte") return resultLoc, result
def genInvCondJump(condLoc, label): ''' Jump if condLoc is 0 ''' if condLoc.getType() != BoolType(): raise SemanticError( condLoc.getPosition(), "Should be a bool type (u8) for a condition, got {}".format( str(condLoc.getType()))) l = condLoc.getIndirLevel() s = condLoc.getSource() assert (l == 0 or l == 1) result = '; jump if not {}\n'.format(condLoc) if l == 0: if s.isNumber(): if not bool(s): result += ''' ldi pl, lo({0}) ldi ph, hi({0}) jmp '''.format(label) else: result += ''' ldi a, lo({0}) add a, 0 ldi pl, lo({1}) ldi ph, hi({1}) jz '''.format(s, label) else: if not s.isRegister(): result += f''' ldi pl, lo({s}) ldi ph, hi({s}) ld a ''' result += f''' ldi pl, lo({label}) ldi ph, hi({label}) add a, 0 jz ''' return result
def generateAssignment(self, l, r, curFn): ''' Assignment. Possible cases: 1. var = expression simply generate the expression into var 2. *(e1) = e2 generate e1 into tmp0 (address) generate e2 into tmp1 put indirect tmp1 at tmp0 3. (*var).field = expression ''' if isinstance(l, Value): # case 1: simple variable if not l.isLValue(): raise SemanticError(l.getPosition(), "Cannot assign to an r-value") dest = l resultLoc, codeExpr = self.generateExpression(r, 0, dest, curFn) if resultLoc == dest: # already assigned where needed return codeExpr else: # need to copy _, codeMove = self.backend.genMove(dest, resultLoc, False) return codeExpr + codeMove elif l.data == 'deref': # case 2: dereferenced expression ptr = l.children[0] self.maxTempVarIndex = max(self.maxTempVarIndex, 1) rvPtr, codePtr = self.generateExpression( ptr, 0, Value.variable(Position.fromAny(ptr), labelname.getTempName(0, curFn)), curFn) assert (not rvPtr.getSource().isRegister()) rvR, codeR = self.generateExpression( r, 1, Value.variable(Position.fromAny(r), labelname.getTempName(1, curFn)), curFn) codePutIndirect = self.backend.genPutIndirect(rvPtr, rvR) return codePtr + codeR + codePutIndirect elif l.data == 'arrow': # case 3: member of a derefed struct ptr = l.children[0] fields = l.children[1:] self.maxTempVarIndex = max(self.maxTempVarIndex, 1) rvPtr, codePtr = self.generateExpression( ptr, 0, Value.variable(Position.fromAny(ptr), labelname.getTempName(0, curFn)), curFn) assert (not rvPtr.getSource().isRegister()) try: offset, type = structure.getField(rvPtr.getType().deref(), fields) except LookupError as e: raise SemanticError(Position.fromAny(r), str(e)) except ValueError as e: raise SemanticError(Position.fromAny(r), str(e)) if type.getIndirectionOffset() < 0: raise SemanticError(Position.fromAny(l), "Cannot assign to an r-value") rvR, codeR = self.generateExpression( r, 1, Value.variable(Position.fromAny(r), labelname.getTempName(1, curFn)), curFn) codePutIndirect = self.backend.genPutIndirect( rvPtr.withType(PtrType(type)), rvR, offset) return codePtr + codeR + codePutIndirect else: raise RuntimeError("Unknown assignment case")
def genMove(resultLoc, srcLoc, avoidCopy): if srcLoc.getType().isUnknown(): raise SemanticError(srcLoc.getPosition(), "Unknown source type") if resultLoc.getType().isUnknown(): resultLoc = resultLoc.removeUnknown(srcLoc.getType()) if resultLoc.getType() != srcLoc.getType(): raise SemanticError( resultLoc.getPosition() - srcLoc.getPosition(), "Incompatible types to assign: {} and {}".format( resultLoc.getType(), srcLoc.getType())) if srcLoc == resultLoc: return resultLoc, "" else: if avoidCopy: return srcLoc, "" else: if resultLoc.getIndirLevel() != 1: raise RuntimeError( "Compiler error: move destination level of indirection is not 1" ) size = srcLoc.getType().getSize() if size == 1 and srcLoc.getSource().isRegister(): assert (srcLoc.getIndirLevel() == 1) rs = resultLoc.getSource() result = f''' ldi pl, lo({rs}) ldi ph, hi({rs}) st a ''' return resultLoc, result elif size <= 2: loadCode = "; {} := {}\n".format(resultLoc, srcLoc) if srcLoc.getIndirLevel() == 0: # var := const c = srcLoc.getSource() loadCode += _loadConst(size, c) else: # var := var loadCode += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld b '''.format(srcLoc.getSource()) if size > 1: loadCode += 'inc pl\n' if not srcLoc.isAligned(): loadCode += ''' mov a, 0 adc ph, a '''.format(srcLoc.getSource()) loadCode += 'ld a\n' storeCode = ''' ldi pl, lo({0}) ldi ph, hi({0}) st b '''.format(resultLoc.getSource()) if size > 1: if resultLoc.isAligned(): storeCode += 'inc pl\n' else: storeCode += ''' ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) '''.format(resultLoc.getSource()) storeCode += 'st a\n' return resultLoc, loadCode + storeCode else: # size > 2 result = f"; {resultLoc} := {srcLoc} (large)\n" if srcLoc.getIndirLevel() == 0: for offset in range(0, size, 2): chunkSize = min(2, size - offset) result += _loadConst(chunkSize, srcLoc.getSource(), offset) result += f""" ldi pl, lo({resultLoc.getSource()} + {offset}) ldi ph, hi({resultLoc.getSource()} + {offset}) st b """ if chunkSize > 1: if resultLoc.isAligned(): result += 'inc pl\n' else: result += f''' ldi pl, lo({resultLoc.getSource()} + {offset + 1}) ldi ph, hi({resultLoc.getSource()} + {offset + 1}) ''' result += 'st a\n' return resultLoc, result else: # TODO loop on large objects for offset in range(0, size, 2): chunkSize = min(2, size - offset) result += f''' ldi pl, lo(({srcLoc.getSource()}) + {offset}) ldi ph, hi(({srcLoc.getSource()}) + {offset}) ld a ''' if chunkSize > 1: if srcLoc.isAligned(): result += f''' inc pl ''' else: result += f''' ldi pl, lo(({srcLoc.getSource()}) + {offset + 1}) ldi ph, hi(({srcLoc.getSource()}) + {offset + 1}) ''' result += f''' ld b ldi pl, lo(({resultLoc.getSource()}) + {offset + 1}) ldi ph, hi(({resultLoc.getSource()}) + {offset + 1}) st b ''' if resultLoc.isAligned(): result += ''' dec pl ''' else: result += f''' ldi pl, lo(({resultLoc.getSource()}) + {offset}) ldi ph, hi(({resultLoc.getSource()}) + {offset}) ''' else: result += f''' ldi pl, lo(({resultLoc.getSource()}) + {offset}) ldi ph, hi(({resultLoc.getSource()}) + {offset}) ''' result += ''' st a ''' return resultLoc, result
def _genSubPtr(resultLoc, src1Loc, src2Loc): if not src2Loc.getType().isInteger(): raise SemanticError(src2Loc.getPosition(), "Can only subtract ponters and integers, not pointers and other types") memberSize = src1Loc.getType().deref().getSize() s1 = src1Loc.getSource() s2 = src2Loc.getSource() rs = resultLoc.getSource() t = resultLoc.getType() result = '; {} = {} - {} * {}\n'.format(resultLoc, src1Loc, src2Loc, memberSize) isWord = src2Loc.getType().getSize() == 2 if memberSize > 2: if src2Loc.getIndirLevel() == 0: pos = src1Loc.getPosition() - src2Loc.getPosition() offset = s2 * memberSize loc, code = _genIntBinary(resultLoc, src1Loc, Value(pos, t, 0, offset, True), "sub", "sbb", "({}) - ({})", operator.sub, False) return loc, result + code elif not isPowerOfTwo(memberSize): raise NatrixNotImplementedError(src2Loc.getPosition(), f"sizeof({src1Loc.getType().deref()}) = {memberSize} is not a power of two") elif src1Loc == resultLoc: shift = log(memberSize) loc, code = _genIncDecPtr(resultLoc, src2Loc, shift, "sub", "sbb") return loc, result + code else: shift = log(memberSize) shiftedLoc, shiftCode = genSHLVarByConst(resultLoc, src2Loc, shift) loc, subtractionCode = _genIntBinary(resultLoc, src1Loc, shiftedLoc, "sub", "sbb", "({}) - ({})", operator.sub, False) return loc, result + shiftCode + subtractionCode elif isWord: assert(not src2Loc.getSource().isRegister()) if memberSize == 1: # no multiplication, just subtract loc, code = _genIntBinary(resultLoc, src1Loc, src2Loc.withType(t), "sub", "sbb", "({}) - ({})", operator.sub, False) return loc, result + code elif memberSize == 2: if src2Loc.getIndirLevel() == 0: pos = src1Loc.getPosition() - src2Loc.getPosition() offset = s2 * 2 loc, code = _genIntBinary(resultLoc, src1Loc, Value(pos, t, 0, offset, True), "sub", "sbb", "({}) - ({})", operator.sub, False) return loc, result + code else: result += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld b '''.format(s2) if src2Loc.isAligned(): result += 'inc pl\n' else: result += ''' ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) '''.format(s2) result += ''' ld a shl a shl b adc a, 0 ''' if src1Loc.getIndirLevel() == 1: result += ''' xor a, b xor b, a xor a, b ldi pl, lo({0}) ldi ph, hi({0}) ld pl sub pl, a mov a, pl ldi ph, hi({0} + 1) ldi pl, lo({0} + 1) ld pl mov ph, a mov a, pl sbb a, b mov b, a mov a, ph '''.format(s1) else: # a:b - index result += ''' mov ph, a ldi a, lo({0}) sub b, a ldi a, hi({0}) sbb ph, a mov a, ph '''.format(s1) result += ''' ldi pl, lo({0}) ldi ph, hi({0}) st a inc pl '''.format(rs) if not resultLoc.isAligned(): result += ''' mov a, 0 adc ph, a ''' result += 'st b\n' else: # not isWord if src2Loc.getIndirLevel() == 0: pos = src1Loc.getPosition() - src2Loc.getPosition() s2 = s2.widen(src2Loc.getType().getSign()) offset = s2 * memberSize loc, code = _genIntBinary(resultLoc, src1Loc, Value(pos, t, 0, offset, True), "sub", "sbb", "({}) - ({})", operator.sub, False) return loc, result + code if src2Loc.getType().getSign(): raise SemanticError(src2Loc.getPosition(), "pointer arithmetic with s8 is not implemented") # s2.indirLevel == 1 result += loadByte('b', src2Loc, 0) if memberSize == 1: if src1Loc.getIndirLevel() == 0: result += ''' ldi a, lo({0}) sub a, b mov b, a ldi a, hi({0}) sbb a, 0 '''.format(src1Loc.getSource()) else: result += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld a sub a, b mov b, a ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) ld a sbb a, 0 '''.format(src1Loc.getSource()) elif memberSize == 2: if src1Loc.getIndirLevel() == 0: result += ''' mov a, 0 mov pl, a shl b adc pl, a ldi a, lo({0}) sub a, b mov b, a ldi a, hi({0}) sbb a, pl '''.format(src1Loc.getSource()) else: result += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld pl ; pl = p_l mov a, 0 shl b ; b = i_l adc a, 0 mov ph, a ; ph = i_h mov a, pl ; a = p_l sub a, b ; a = r_l mov b, a ; b = r_l mov a, 0 adc a, 0 ; a = c add a, ph ; a = i_h + c ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) ld pl ; pl = p_h sub pl, a mov a, pl ; a = p_h - (i_h + c) = r_h '''.format(src1Loc.getSource()) else: raise RuntimeError("Other sizes than 1 and 2 are not supported for pointer indexing") result += ''' ldi pl, lo({0}) ldi ph, hi({0}) st b '''.format(resultLoc.getSource()) if resultLoc.isAligned(): result += 'inc pl\n' else: result += ''' ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) '''.format(resultLoc.getSource()) result += 'st a\n' return resultLoc, result
def int_type(self, s): try: return IntType.parse(s) except ValueError as e: raise SemanticError(Position.fromAny(t), str(e))
def _genIntBinary(resultLoc, src1Loc, src2Loc, opLo, opHi, pyPattern, constLambda, carryIrrelevant): if resultLoc.getType().isUnknown(): resultLoc = resultLoc.removeUnknown(src1Loc.getType()) if resultLoc.getType() != src1Loc.getType(): raise SemanticError(resultLoc.getPosition(), "Incompatible result and source types: {} and {}".format(resultLoc.getType(), src1Loc.getType())) if src1Loc.getType() != src2Loc.getType(): raise SemanticError(resultLoc.getPosition(), "Incompatible source types: {} and {}".format(src1Loc.getType(), src2Loc.getType())) assert(resultLoc.getIndirLevel() == 1) l1 = src1Loc.getIndirLevel() l2 = src2Loc.getIndirLevel() assert(l1 == 0 or l1 == 1) assert(l2 == 0 or l2 == 1) if l1 == 0 and l2 == 0: raise RuntimeError("Case unhandled by ConstTransformer") t = resultLoc.getType() rs = resultLoc.getSource() s1 = src1Loc.getSource() s2 = src2Loc.getSource() result = "; {} = {} {}, {}\n".format(resultLoc, opLo, src1Loc, src2Loc) if t.getSize() <= 2: isWord = t.getSize() == 2 if not isWord: assert(not s1.isRegister() or not s2.isRegister()) result += loadByte('b', src1Loc, 0) result += loadByte('a', src2Loc, 0) result += f''' {opLo} b, a mov a, b ''' return Value.register(resultLoc.getPosition(), t), result # now it's a word if l2 == 0: # var + const result += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld b '''.format(src1Loc.getSource()) result += incP(src1Loc.isAligned()) c = src2Loc.getSource() result += loadByte('a', src2Loc, 0) result += f''' {opLo} b, a ld a ''' result += loadByte('pl', src2Loc, 1) result += f'{opHi} a, pl\n' result += ''' ldi pl, lo({0}) ldi ph, hi({0}) st b '''.format(rs) if resultLoc.isAligned(): result += ''' inc pl st a ''' else: result += f''' ldi pl, lo({rs} + 1) ldi ph, hi({rs} + 1) st a ''' elif l1 == 0: # const + var result += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld a '''.format(src2Loc.getSource()) if src2Loc.isAligned(): result += 'inc pl\n' else: result += ''' ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) '''.format(src2Loc.getSource()) result += loadByte('b', src1Loc, 0) result += f''' {opLo} b, a ld pl ''' result += loadByte('a', src1Loc, 1) result += f''' {opHi} a, pl ldi pl, lo({rs}) ldi ph, hi({rs}) st b '''.format(rs) if resultLoc.isAligned(): result += ''' inc pl st a ''' else: result += f''' ldi pl, lo({rs} + 1) ldi ph, hi({rs} + 1) st a ''' else: # var + var s1 = src1Loc.getSource() s2 = src2Loc.getSource() result += ''' ldi pl, lo({0}) ldi ph, hi({0}) ld b ldi pl, lo({1}) ldi ph, hi({1}) ld a {2} b, a '''.format(s1, s2, opLo) if isWord: result += ''' ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) ld a ldi pl, lo({1} + 1) ldi ph, hi({1} + 1) ld pl {2} a, pl '''.format(s1, s2, opHi) result += ''' ldi pl, lo({0}) ldi ph, hi({0}) st b '''.format(rs) if isWord: if resultLoc.isAligned(): result += ''' inc pl st a ''' else: result += ''' ldi pl, lo({0} + 1) ldi ph, hi({0} + 1) st a '''.format(rs) else: # size > 2 # This produces less size-efficient code than above, that's why it's not a general case. for offset in range(0, t.getSize(), 2): rest = t.getSize() - offset result += loadByte('a', src1Loc, offset) if rest > 1: if l1 == 0: result += loadByte('b', src1Loc, offset + 1) else: if (offset == 0 or carryIrrelevant) and src1Loc.isAligned(): # can trash flags if it's the first pair of bytes or if op is like and, or, xor result += ''' inc pl ld b ''' else: # must preserve flags if src1Loc.isAligned(): result += f''' ldi pl, lo({s1} + {offset + 1}) ld b ''' else: result += loadByte('b', src1Loc, offset + 1) result += loadByte('pl', src2Loc, offset) if offset == 0: result += f'{opLo} a, pl\n' else: result += f'{opHi} a, pl\n' result += f''' ldi pl, lo({rs} + {offset}) ldi ph, hi({rs} + {offset}) st a ''' if rest > 1: result += loadByte('a', src2Loc, offset + 1) result += f''' {opHi} b, a ldi pl, lo({rs} + {offset + 1}) ldi ph, hi({rs} + {offset + 1}) st b ''' return resultLoc, result
def generateStatement(self, t, curFn): pos = Position.fromAny(t) f, l = self.lineInfo.translatePosition(pos) result = self.backend.sourceFilename(f) result += self.backend.lineNumber(l) if t.data == 'assignment': l, r = t.children result += self.generateAssignment(l, r, curFn) elif t.data == 'decl_var': result += "" elif t.data == 'block': result += "".join( self.generateStatement(child, curFn) for child in t.children) elif t.data == 'conditional': result += self.generateConditional( t.children[0], t.children[1], t.children[2] if len(t.children) == 3 else None, curFn) elif t.data == 'while_loop': result += self.generateWhile(t.children[0], t.children[1], curFn) elif t.data == 'for_loop': result += self.generateFor(t.children[0], t.children[1], t.children[2], t.children[3], curFn) elif t.data == 'break_statement': if len(self.breakLabel) == 0: raise SemanticError(t.line, "Not in a loop") result += self.backend.genJump(self.breakLabel[0]) elif t.data == 'continue_statement': if len(self.continueLabel) == 0: raise SemanticError(t.line, "Not in a loop") result += self.backend.genJump(self.continueLabel[0]) elif t.data == 'function_call': result += self.generateFunctionCall(Position.fromAny(t), str(t.children[0]), t.children[1:], curFn) elif t.data == 'assignment_function': call = t.children[1] result += self.generateFunctionAssignment( Position.fromAny(t.children[0]), Position.fromAny(call), t.children[0], str(call.children[0]), call.children[1:], curFn) elif t.data == 'return_statement': dest = Value.variable(Position.fromAny(t), labelname.getReturnName(curFn, False), self.nameInfo.functions[curFn].retType) result += self.generateAssignment(dest, t.children[0], curFn) + self.backend.genReturn( curFn, self.useStack) elif t.data == 'empty_return_statement': result += self.backend.genReturn(curFn, self.useStack) elif t.data == 'return_fc_statement': call = t.children[0] dest = Value.variable(Position.fromAny(t), labelname.getReturnName(curFn, False), self.nameInfo.functions[curFn].retType) callCode = self.generateFunctionAssignment(Position.fromAny(t), Position.fromAny(call), dest, str(call.children[0]), call.children[1:], curFn) retCode = self.backend.genReturn(curFn, self.useStack) result += callCode + retCode return result
def generateExpression(self, t, minTempVarIndex, resultLoc, curFn): ''' Generate code to compute the expression. :param t: the expression tree :param minTempVarIndex: minimum index of a temp variable to use :param resultLoc: where to place the result. The result can be placed somewhere else (TODO). :param curFn: name of the current function. :return: the actual location of the result ''' try: if isinstance(t, Value): return self.backend.genMove(resultLoc, t, True) else: ch = t.children if len(ch) == 1: if isinstance(ch[0], Value): argCode = "" rv = ch[0] else: self.maxTempVarIndex = max(self.maxTempVarIndex, minTempVarIndex) rv, argCode = self.generateExpression( ch[0], minTempVarIndex, Value.variable( Position.fromAny(ch[0]), labelname.getTempName(minTempVarIndex, curFn)), curFn) resultLoc, myCode = self.backend.genUnary( t.data, resultLoc, rv) return resultLoc, argCode + myCode elif t.data == "type_cast": if isinstance(ch[1], Value): argCode = "" rv = ch[1] else: self.maxTempVarIndex = max(self.maxTempVarIndex, minTempVarIndex) rv, argCode = self.generateExpression( ch[1], minTempVarIndex, Value.variable( Position.fromAny(ch[1]), labelname.getTempName(minTempVarIndex, curFn)), curFn) resultLoc, myCode = self.backend.genCast( resultLoc, ch[0], rv) return resultLoc, argCode + myCode elif t.data == "arrow": ptr = ch[0] fields = ch[1:] self.maxTempVarIndex = max(self.maxTempVarIndex, minTempVarIndex) rv, argCode = self.generateExpression( ptr, minTempVarIndex, Value.variable( Position.fromAny(ptr), labelname.getTempName(minTempVarIndex, curFn)), curFn) offset, type = structure.getField(rv.getType().deref(), fields) if type.getIndirectionOffset() == 0: resultLoc, derefCode = self.backend.genDeref( resultLoc.withType(type), rv, offset) return resultLoc, argCode + derefCode elif type.getIndirectionOffset() == -1: rv, offsetCode = self.backend.genAddPointerOffset( resultLoc.withType(type), rv.withType(type), offset) return rv, argCode + offsetCode else: raise RuntimeError("Wrong indirection offset") elif t.data == "p_arrow": ptr = ch[0] fields = ch[1:] self.maxTempVarIndex = max(self.maxTempVarIndex, minTempVarIndex) rv, argCode = self.generateExpression( ptr, minTempVarIndex, resultLoc.withType(UnknownType()), curFn) offset, type = structure.getField(rv.getType().deref(), fields) if type.getIndirectionOffset() < 0: raise SemanticError(Position.fromAny(t), "Cannot get address") rv, offsetCode = self.backend.genAddPointerOffset( resultLoc.withType(PtrType(type)), rv.withType(PtrType(type)), offset) return rv, argCode + offsetCode elif len(ch) == 2: hasFirstArg = False if isinstance(ch[0], Value): rv1 = ch[0] argCode1 = "" else: self.maxTempVarIndex = max(self.maxTempVarIndex, minTempVarIndex) tmp0 = Value.variable( Position.fromAny(ch[0]), labelname.getTempName(minTempVarIndex, curFn)) rv1, argCode1 = self.generateExpression( ch[0], minTempVarIndex, tmp0, curFn) hasFirstArg = True if isinstance(ch[1], Value): rv2 = ch[1] argCode2 = "" else: indexIncrement = 1 if hasFirstArg else 0 self.maxTempVarIndex = max( self.maxTempVarIndex, minTempVarIndex + indexIncrement) tmp1 = Value.variable( Position.fromAny(ch[1]), labelname.getTempName( minTempVarIndex + indexIncrement, curFn)) rv2, argCode2 = self.generateExpression( ch[1], minTempVarIndex + indexIncrement, tmp1, curFn) if rv1.getSource().isRegister(): rv1, moveCode = self.backend.genMove( tmp0, rv1, False) argCode1 += moveCode try: resultLoc, myCode = self.backend.genBinary( t.data, resultLoc, rv1, rv2, self) except RegisterNotSupportedError as e: if e.argNumber == 0: rv1, moveCode = self.backend.genMove( tmp0, rv1, False) argCode1 += moveCode elif e.argNumber == 1: rv2, moveCode = self.backend.genMove( tmp1, rv2, False) argCode2 += moveCode else: raise resultLoc, myCode = self.backend.genBinary( t.data, resultLoc, rv1, rv2, self) return resultLoc, argCode1 + argCode2 + myCode else: raise RuntimeError("Too many children") except LookupError as e: raise SemanticError(Position.fromAny(t), str(e)) except ValueError as e: raise SemanticError(Position.fromAny(t), str(e))
def genMul(resultLoc, src1Loc, src2Loc): if src1Loc.getType() != src2Loc.getType(): raise SemanticError(src1Loc.getPosition() - src2Loc.getPosition(), "multiplication types mismatch") t = src1Loc.getType() resultLoc = resultLoc.withType(t) if t.getSize() not in {1, 2, 4}: raise NatrixNotImplementedError( src1Loc.getPosition(), f"multiplication of {t.getSize() * 8}-bit integers is not implemented" ) l1 = src1Loc.getIndirLevel() l2 = src2Loc.getIndirLevel() if l1 == 0 and l2 == 0: raise NotImplementedError("doing shit with pointers?") elif l1 == 0: s = src1Loc.getSource() if s.isNumber(): return _genMulVC(resultLoc, src2Loc, int(s)) else: raise NotImplementedError("doing shit with pointers?") elif l2 == 0: s = src2Loc.getSource() if s.isNumber(): return _genMulVC(resultLoc, src1Loc, int(s)) else: raise NotImplementedError("doing shit with pointers?") else: result = '; {} = {} * {}\n'.format(resultLoc, src1Loc, src2Loc) if t.getSize() == 2: result += copyW(src1Loc.getSource(), "__cc_r_a", src1Loc.isAligned(), True) result += copyW(src2Loc.getSource(), "__cc_r_b", src2Loc.isAligned(), True) result += call("__cc_mul_word") result += copyW("__cc_r_r", resultLoc.getSource(), True, resultLoc.isAligned()) return resultLoc, result elif t.getSize() == 1: result += loadByte('b', src1Loc, 0) result += loadByte('a', src2Loc, 0) result += ''' ldi pl, lo(__cc_r_a) ldi ph, hi(__cc_r_a) st b ldi pl, lo(__cc_r_b) st a ''' result += call("__cc_mul_byte") result += ''' ldi pl, lo(__cc_r_r) ldi ph, hi(__cc_r_r) ld a ''' return Value.register( src1Loc.getPosition() - src2Loc.getPosition(), t), result elif t.getSize() == 4: result += copyW(src1Loc.getSource(), "__cc_r_a", src1Loc.isAligned(), True, 0) result += copyW(src1Loc.getSource(), "__cc_r_a", src1Loc.isAligned(), True, 2) result += copyW(src2Loc.getSource(), "__cc_r_b", src1Loc.isAligned(), True, 0) result += copyW(src2Loc.getSource(), "__cc_r_b", src1Loc.isAligned(), True, 2) result += call("__cc_mul_dword") result += copyW("__cc_r_r", resultLoc.getSource(), True, resultLoc.isAligned(), 0) result += copyW("__cc_r_r", resultLoc.getSource(), True, resultLoc.isAligned(), 2) return resultLoc, result