async def compile(self, ctx: CompileContext) -> Register: lhs, rhs = await self.compile_meta(ctx) res = ctx.get_register(lhs.size, self.sign) op = {"+": "add", "-": "sub"}[self.op] if isinstance(await self.type, Pointer): # adding a pointer with an integer multiplies the integer side by the pointed to type (ptr_type, non_ptr) = ((self.left_type, rhs) if isinstance( self.left_type, Pointer) else (self.right_type, lhs)) if isinstance(ptr_type.to, Void): raise self.error("Cannot perform pointer arithmetic on pointer to void.") ctx.emit(Binary.mul(non_ptr, Immediate(ptr_type.to.size, non_ptr.size))) elif (op == "sub" and isinstance(self.left_type, Pointer) and isinstance(self.right_type, Pointer)): if isinstance(self.left_type.to, Void): raise self.error("Cannot perform pointer arithmetic on pointer to void.") if self.left_type != self.right_type: raise self.error("Both sides of pointer subtraction must be the same type.") # subtracting two pointers of equal type yields the number of elements between them ctx.emit(Binary(lhs, rhs, op, res)) ctx.emit(Binary.udiv(res, Immediate(self.left_type.to.size, res.size))) return res ctx.emit(Binary(lhs, rhs, op, res)) return res
async def compile_as_arr(self, ctx: CompileContext) -> Register: """Compile an array literal but inline the inner values.""" if (isinstance((await self.type).to, types.Array) and (not isinstance(self.first_elem, ArrayLiteral))): # Maybe just cast the internal type to a pointer. raise self.error( "Internal type is of array type but is not a literal.") if self.var is None: self.var = ctx.declare_unique_variable(await self.type) self.var.lvalue_is_rvalue = True base = ctx.get_register(types.Pointer.size) index = ctx.get_register(types.Pointer.size) ctx.emit(LoadVar(self.var, base)) ctx.emit(Mov(index, base)) elem_size = (await self.type).to.size for i in self.exprs: await i.compile_as_arr_helper(ctx, index) # NOTE: will we ever hit this? if self.float_size: ctx.emit(Binary.add(index, Immediate(elem_size * self.float_size))) return base
async def compile(self, ctx: CompileContext) -> Register: op = {"|": "or", "^": "xor", "&": "and"}[self.op] lhs, rhs = await self.compile_meta(ctx) res = ctx.get_register(lhs.size, self.sign) ctx.emit(Binary(lhs, rhs, op, res)) return res
async def compile_as_ref(self, ctx: CompileContext) -> Register: """Compile to the array literal and return a reference to the start. This function is for when an array of references is the wanted outcome. This function will not work on array types. """ if self.var is None: self.var = ctx.declare_unique_variable(await self.type) self.var.lvalue_is_rvalue = True if (isinstance(self.first_elem, ArrayLiteral) and (not isinstance( (await self.type).to, types.Pointer))): raise self.error( "Cannot compile to references if internal array type is an array and not a pointer" ) base = ctx.get_register(types.Pointer.size) index = ctx.get_register(types.Pointer.size) ctx.emit(LoadVar(self.var, base)) ctx.emit(Mov(index, base)) elem_type = (await self.type).to for i in self.exprs: r = await i.compile(ctx) if r.size != elem_type.size: r0 = r.resize(elem_type.size, elem_type.signed) ctx.emit(Resize(r, r0)) r = r0 ctx.emit(Mov(Dereference(index, r.size), r)) ctx.emit(Binary.add(index, Immediate(r.size, types.Pointer.size))) if self.float_size: # fill in missing values ctx.emit( Binary.add( index, Immediate(elem_type.size * self.float_size, index.size))) return base
async def compile(self, ctx: CompileContext) -> Register: my_type = await self.type ptr: Register = await self.load_lvalue(ctx) tmp = ctx.get_register(my_type.size, my_type.signed) increment = 1 # in the case of pointer increments, increment by the size of the pointer's underlying type if isinstance(my_type, Pointer): increment = my_type.to.size ctx.emit(Mov(tmp, Dereference(ptr, tmp.size))) ctx.emit(Binary(tmp, Immediate(increment, tmp.size), self.op)) ctx.emit(Mov(Dereference(ptr, tmp.size), tmp)) return tmp
async def load_lvalue(self, ctx: CompileContext) -> Register: atype = await self.arg.type if not isinstance(atype, (Pointer, Array)): raise self.error(f"Incompatible type to array index base {atype}") # don't allow void pointer arithmetic # This 'would' fail later with an error about requesting a register of size 0 # But that would be less informative than catching the error now. if isinstance(atype.to, Void): raise self.error("Cannot perform pointer arithemetic on void pointers.") argument = await self.arg.compile(ctx) offset = await self.offset.compile(ctx) # get the size of the inner type if type.to is an array, # this will be the size of the internal array size = await self.size # make sure both the offset and the arguments are the correct size (size of a pointer) if argument.size != Pointer.size: argument0 = argument.resize(Pointer.size) ctx.emit(Resize(argument, argument0)) argument = argument0 if offset.size != Pointer.size: offset0 = offset.resize(Pointer.size) ctx.emit(Resize(offset, offset0)) offset = offset0 result = ctx.get_register(Pointer.size) # multiply to the size of the inner type of the pointer/ array ctx.emit(Binary.mul(offset, Immediate(size, offset.size))) ctx.emit(Binary.add(argument, offset, result)) return result
async def compile_as_arr_helper(self, ctx: CompileContext, base: Register): if isinstance(self.first_elem, ArrayLiteral) and isinstance( await self.type, types.Array): for i in self.exprs: await i.compile_as_arr_helper(ctx, base) else: elem_type = (await self.type).to for i in self.exprs: r = await i.compile(ctx) if r.size != elem_type.size: r0 = r.resize(elem_type.size, elem_type.signed) ctx.emit(Resize(r, r0)) r = r0 ctx.emit(Mov(Dereference(base, r.size), r)) ctx.emit( Binary.add(base, Immediate(r.size, types.Pointer.size)))
async def compile(self, ctx: CompileContext) -> Register: my_type = await self.type ptr: Register = await self.arg.load_lvalue(ctx) size = my_type.size res, temp = (ctx.get_register(size, my_type.signed), ctx.get_register(size)) increment = 1 if isinstance(my_type, Pointer): increment = my_type.to.size ctx.emit(Mov(res, Dereference(ptr, res.size))) ctx.emit(Binary(res, Immediate(increment, size), self.op, temp)) ctx.emit(Mov(Dereference(ptr, temp.size), temp)) return res
async def compile(self, ctx: CompileContext) -> Register: lhs, rhs = await self.compile_meta(ctx) if rhs.sign: raise self.right.error( "RHS operand to a binary shift op must be unsigned.") res = ctx.get_register(lhs.size, self.sign) if self.op == "<<": op = "shl" elif self.op == ">>" and lhs.sign: op = "sar" else: op = "shr" ctx.emit(Binary(lhs, rhs, op, res)) return res
async def compile(self, ctx: CompileContext) -> Register: lhs, rhs = await self.compile_meta(ctx) res = ctx.get_register(lhs.size, self.sign) if self.op == "*": op = "mul" elif self.op == "%": if self.sign: op = "imod" else: op = "umod" elif self.op == "/" and self.sign: op = "idiv" else: op = "udiv" ctx.emit(Binary(lhs, rhs, op, res)) return res