forked from M-Labs/artiq
1
0
Fork 0

compiler: Don't store redundant ndarray buffer length, match list layout

This adds `elt` to _TPointer and the ir.Offset IR instruction,
which is like GetElem but without the final load.
This commit is contained in:
David Nadlinger 2020-07-27 23:21:23 +01:00
parent e82357d180
commit dea3c0c572
6 changed files with 66 additions and 40 deletions

View File

@ -93,8 +93,8 @@ class TArray(types.TMono):
super().__init__("array", {"elt": elt, "num_dims": num_dims}) super().__init__("array", {"elt": elt, "num_dims": num_dims})
self.attributes = OrderedDict([ self.attributes = OrderedDict([
("buffer", types._TPointer(elt)),
("shape", types.TTuple([TInt32()] * num_dims.value)), ("shape", types.TTuple([TInt32()] * num_dims.value)),
("buffer", TList(elt)),
]) ])
def _array_printer(typ, printer, depth, max_depth): def _array_printer(typ, printer, depth, max_depth):
@ -317,7 +317,7 @@ def is_iterable(typ):
def get_iterable_elt(typ): def get_iterable_elt(typ):
if is_str(typ) or is_bytes(typ) or is_bytearray(typ): if is_str(typ) or is_bytes(typ) or is_bytearray(typ):
return TInt(types.TValue(8)) return TInt(types.TValue(8))
elif is_iterable(typ): elif types._is_pointer(typ) or is_iterable(typ):
return typ.find()["elt"].find() return typ.find()["elt"].find()
else: else:
assert False assert False

View File

@ -738,6 +738,33 @@ class SetAttr(Instruction):
def value(self): def value(self):
return self.operands[1] return self.operands[1]
class Offset(Instruction):
"""
An intruction that adds an offset to a pointer (indexes into a list).
This is used to represent internally generated pointer arithmetic, and must
remain inside the same object (see :class:`GetElem` and LLVM's GetElementPtr).
"""
"""
:param lst: (:class:`Value`) list
:param index: (:class:`Value`) index
"""
def __init__(self, base, offset, name=""):
assert isinstance(base, Value)
assert isinstance(offset, Value)
typ = types._TPointer(builtins.get_iterable_elt(base.type))
super().__init__([base, offset], typ, name)
def opcode(self):
return "offset"
def base(self):
return self.operands[0]
def index(self):
return self.operands[1]
class GetElem(Instruction): class GetElem(Instruction):
""" """
An intruction that loads an element from a list. An intruction that loads an element from a list.
@ -755,7 +782,7 @@ class GetElem(Instruction):
def opcode(self): def opcode(self):
return "getelem" return "getelem"
def list(self): def base(self):
return self.operands[0] return self.operands[0]
def index(self): def index(self):
@ -781,7 +808,7 @@ class SetElem(Instruction):
def opcode(self): def opcode(self):
return "setelem" return "setelem"
def list(self): def base(self):
return self.operands[0] return self.operands[0]
def index(self): def index(self):
@ -840,6 +867,7 @@ class Arith(Instruction):
def rhs(self): def rhs(self):
return self.operands[1] return self.operands[1]
class Compare(Instruction): class Compare(Instruction):
""" """
A comparison operation on numbers. A comparison operation on numbers.

View File

@ -523,12 +523,11 @@ class ARTIQIRGenerator(algorithm.Visitor):
lengths[1:], lengths[0]) lengths[1:], lengths[0])
offset = self.append(ir.Arith(ast.Mult(loc=None), stride, index)) offset = self.append(ir.Arith(ast.Mult(loc=None), stride, index))
old_buffer = self.append(ir.GetAttr(value, "buffer")) old_buffer = self.append(ir.GetAttr(value, "buffer"))
# KLUDGE: Represent offsetting by Alloc with two arguments. new_buffer = self.append(ir.Offset(old_buffer, offset))
new_buffer = self.append(ir.Alloc([old_buffer, offset], old_buffer.type))
result_type = builtins.TArray(value.type.find()["elt"], result_type = builtins.TArray(value.type.find()["elt"],
types.TValue(num_dims - 1)) types.TValue(num_dims - 1))
return self.append(ir.Alloc([new_shape, new_buffer], result_type)) return self.append(ir.Alloc([new_buffer, new_shape], result_type))
else: else:
buffer = self.append(ir.GetAttr(value, "buffer")) buffer = self.append(ir.GetAttr(value, "buffer"))
return self.append(ir.GetElem(buffer, index)) return self.append(ir.GetElem(buffer, index))
@ -1740,7 +1739,7 @@ class ARTIQIRGenerator(algorithm.Visitor):
ir.Constant(0, self._size_type), lambda index: self.append( ir.Constant(0, self._size_type), lambda index: self.append(
ir.Compare(ast.Lt(loc=None), index, num_total_elts)), body_gen) ir.Compare(ast.Lt(loc=None), index, num_total_elts)), body_gen)
return self.append(ir.Alloc([shape, buffer], node.type)) return self.append(ir.Alloc([buffer, shape], node.type))
else: else:
assert False assert False
elif types.is_builtin(typ, "range"): elif types.is_builtin(typ, "range"):

View File

@ -33,7 +33,8 @@ class DeadCodeEliminator:
# it also has to run after the interleaver, but interleaver # it also has to run after the interleaver, but interleaver
# doesn't like to work with IR before DCE. # doesn't like to work with IR before DCE.
if isinstance(insn, (ir.Phi, ir.Alloc, ir.GetAttr, ir.GetElem, ir.Coerce, if isinstance(insn, (ir.Phi, ir.Alloc, ir.GetAttr, ir.GetElem, ir.Coerce,
ir.Arith, ir.Compare, ir.Select, ir.Quote, ir.Closure)) \ ir.Arith, ir.Compare, ir.Select, ir.Quote, ir.Closure,
ir.Offset)) \
and not any(insn.uses): and not any(insn.uses):
insn.erase() insn.erase()
modified = True modified = True

View File

@ -211,7 +211,7 @@ class LLVMIRGenerator:
else: else:
return llunit return llunit
elif types._is_pointer(typ): elif types._is_pointer(typ):
return llptr return ll.PointerType(self.llty_of_type(typ["elt"]))
elif types.is_function(typ): elif types.is_function(typ):
sretarg = [] sretarg = []
llretty = self.llty_of_type(typ.ret, for_return=True) llretty = self.llty_of_type(typ.ret, for_return=True)
@ -249,7 +249,7 @@ class LLVMIRGenerator:
elif builtins.is_array(typ): elif builtins.is_array(typ):
llshapety = self.llty_of_type(typ.attributes["shape"]) llshapety = self.llty_of_type(typ.attributes["shape"])
llbufferty = self.llty_of_type(typ.attributes["buffer"]) llbufferty = self.llty_of_type(typ.attributes["buffer"])
return ll.LiteralStructType([llshapety, llbufferty]) return ll.LiteralStructType([llbufferty, llshapety])
elif builtins.is_listish(typ): elif builtins.is_listish(typ):
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ)) lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
return ll.LiteralStructType([lleltty.as_pointer(), lli32]) return ll.LiteralStructType([lleltty.as_pointer(), lli32])
@ -737,28 +737,13 @@ class LLVMIRGenerator:
name=insn.name) name=insn.name)
else: else:
assert False assert False
elif builtins.is_listish(insn.type) and not builtins.is_array(insn.type): elif types._is_pointer(insn.type) or (builtins.is_listish(insn.type)
if builtins.is_listish(insn.operands[0].type): and not builtins.is_array(insn.type)):
# KLUDGE: Offsetting is represented as Alloc with base list in the first
# argument and offset in the second. Should probably move this to a
# seprate node type (or make it possible to construct lists from
# pointer/length).
llbase = self.map(insn.operands[0])
lloldbase = self.llbuilder.extract_value(llbase, 0)
lloldsize = self.llbuilder.extract_value(llbase, 1)
lloffset = self.map(insn.operands[1])
llbase = self.llbuilder.gep(lloldbase, [lloffset], inbounds=True)
llsize = self.llbuilder.sub(lloldsize, lloffset)
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
llvalue = self.llbuilder.insert_value(llvalue, llbase, 0)
llvalue = self.llbuilder.insert_value(llvalue, llsize, 1)
return llvalue
llsize = self.map(insn.operands[0]) llsize = self.map(insn.operands[0])
lleltty = self.llty_of_type(builtins.get_iterable_elt(insn.type)) lleltty = self.llty_of_type(builtins.get_iterable_elt(insn.type))
llalloc = self.llbuilder.alloca(lleltty, size=llsize) llalloc = self.llbuilder.alloca(lleltty, size=llsize)
if types._is_pointer(insn.type):
return llalloc
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined) llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
llvalue = self.llbuilder.insert_value(llvalue, llalloc, 0, name=insn.name) llvalue = self.llbuilder.insert_value(llvalue, llalloc, 0, name=insn.name)
llvalue = self.llbuilder.insert_value(llvalue, llsize, 1) llvalue = self.llbuilder.insert_value(llvalue, llsize, 1)
@ -962,20 +947,28 @@ class LLVMIRGenerator:
inbounds=True, name=insn.name) inbounds=True, name=insn.name)
return self.llbuilder.store(llvalue, llptr) return self.llbuilder.store(llvalue, llptr)
def process_GetElem(self, insn): def process_Offset(self, insn):
lst, idx = insn.list(), insn.index() base, idx = insn.base(), insn.index()
lllst, llidx = map(self.map, (lst, idx)) llelts, llidx = map(self.map, (base, idx))
llelts = self.llbuilder.extract_value(lllst, 0) if not types._is_pointer(base.type):
# This is list-ish.
llelts = self.llbuilder.extract_value(llelts, 0)
llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True) llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True)
return llelt
def process_GetElem(self, insn):
llelt = self.process_Offset(insn)
llvalue = self.llbuilder.load(llelt) llvalue = self.llbuilder.load(llelt)
if isinstance(llvalue.type, ll.PointerType): if isinstance(llvalue.type, ll.PointerType):
self.mark_dereferenceable(llvalue) self.mark_dereferenceable(llvalue)
return llvalue return llvalue
def process_SetElem(self, insn): def process_SetElem(self, insn):
lst, idx = insn.list(), insn.index() base, idx = insn.base(), insn.index()
lllst, llidx = map(self.map, (lst, idx)) llelts, llidx = map(self.map, (base, idx))
llelts = self.llbuilder.extract_value(lllst, 0) if not types._is_pointer(base.type):
# This is list-ish.
llelts = self.llbuilder.extract_value(llelts, 0)
llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True) llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True)
return self.llbuilder.store(self.map(insn.value()), llelt) return self.llbuilder.store(self.map(insn.value()), llelt)
@ -1190,7 +1183,8 @@ class LLVMIRGenerator:
collection, = insn.operands collection, = insn.operands
if builtins.is_array(collection.type): if builtins.is_array(collection.type):
# Return length of outermost dimension. # Return length of outermost dimension.
shape = self.llbuilder.extract_value(self.map(collection), 0) shape = self.llbuilder.extract_value(self.map(collection),
self.attr_index(collection.type, "shape"))
return self.llbuilder.extract_value(shape, 0) return self.llbuilder.extract_value(shape, 0)
return self.llbuilder.extract_value(self.map(collection), 1) return self.llbuilder.extract_value(self.map(collection), 1)
elif insn.op in ("printf", "rtio_log"): elif insn.op in ("printf", "rtio_log"):

View File

@ -204,8 +204,10 @@ class TTuple(Type):
return hash(tuple(self.elts)) return hash(tuple(self.elts))
class _TPointer(TMono): class _TPointer(TMono):
def __init__(self): def __init__(self, elt=None):
super().__init__("pointer") if elt is None:
elt = TMono("int", {"width": 8}) # i8*
super().__init__("pointer", params={"elt": elt})
class TFunction(Type): class TFunction(Type):
""" """
@ -735,6 +737,8 @@ class TypePrinter(object):
else: else:
return "%s(%s)" % (typ.name, ", ".join( return "%s(%s)" % (typ.name, ", ".join(
["%s=%s" % (k, self.name(typ.params[k], depth + 1)) for k in typ.params])) ["%s=%s" % (k, self.name(typ.params[k], depth + 1)) for k in typ.params]))
elif isinstance(typ, _TPointer):
return "{}*".format(self.name(typ["elt"], depth + 1))
elif isinstance(typ, TTuple): elif isinstance(typ, TTuple):
if len(typ.elts) == 1: if len(typ.elts) == 1:
return "(%s,)" % self.name(typ.elts[0], depth + 1) return "(%s,)" % self.name(typ.elts[0], depth + 1)