forked from M-Labs/artiq
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:
parent
e82357d180
commit
dea3c0c572
|
@ -93,8 +93,8 @@ class TArray(types.TMono):
|
|||
|
||||
super().__init__("array", {"elt": elt, "num_dims": num_dims})
|
||||
self.attributes = OrderedDict([
|
||||
("buffer", types._TPointer(elt)),
|
||||
("shape", types.TTuple([TInt32()] * num_dims.value)),
|
||||
("buffer", TList(elt)),
|
||||
])
|
||||
|
||||
def _array_printer(typ, printer, depth, max_depth):
|
||||
|
@ -317,7 +317,7 @@ def is_iterable(typ):
|
|||
def get_iterable_elt(typ):
|
||||
if is_str(typ) or is_bytes(typ) or is_bytearray(typ):
|
||||
return TInt(types.TValue(8))
|
||||
elif is_iterable(typ):
|
||||
elif types._is_pointer(typ) or is_iterable(typ):
|
||||
return typ.find()["elt"].find()
|
||||
else:
|
||||
assert False
|
||||
|
|
|
@ -738,6 +738,33 @@ class SetAttr(Instruction):
|
|||
def value(self):
|
||||
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):
|
||||
"""
|
||||
An intruction that loads an element from a list.
|
||||
|
@ -755,7 +782,7 @@ class GetElem(Instruction):
|
|||
def opcode(self):
|
||||
return "getelem"
|
||||
|
||||
def list(self):
|
||||
def base(self):
|
||||
return self.operands[0]
|
||||
|
||||
def index(self):
|
||||
|
@ -781,7 +808,7 @@ class SetElem(Instruction):
|
|||
def opcode(self):
|
||||
return "setelem"
|
||||
|
||||
def list(self):
|
||||
def base(self):
|
||||
return self.operands[0]
|
||||
|
||||
def index(self):
|
||||
|
@ -840,6 +867,7 @@ class Arith(Instruction):
|
|||
def rhs(self):
|
||||
return self.operands[1]
|
||||
|
||||
|
||||
class Compare(Instruction):
|
||||
"""
|
||||
A comparison operation on numbers.
|
||||
|
|
|
@ -523,12 +523,11 @@ class ARTIQIRGenerator(algorithm.Visitor):
|
|||
lengths[1:], lengths[0])
|
||||
offset = self.append(ir.Arith(ast.Mult(loc=None), stride, index))
|
||||
old_buffer = self.append(ir.GetAttr(value, "buffer"))
|
||||
# KLUDGE: Represent offsetting by Alloc with two arguments.
|
||||
new_buffer = self.append(ir.Alloc([old_buffer, offset], old_buffer.type))
|
||||
new_buffer = self.append(ir.Offset(old_buffer, offset))
|
||||
|
||||
result_type = builtins.TArray(value.type.find()["elt"],
|
||||
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:
|
||||
buffer = self.append(ir.GetAttr(value, "buffer"))
|
||||
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.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:
|
||||
assert False
|
||||
elif types.is_builtin(typ, "range"):
|
||||
|
|
|
@ -33,7 +33,8 @@ class DeadCodeEliminator:
|
|||
# it also has to run after the interleaver, but interleaver
|
||||
# doesn't like to work with IR before DCE.
|
||||
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):
|
||||
insn.erase()
|
||||
modified = True
|
||||
|
|
|
@ -211,7 +211,7 @@ class LLVMIRGenerator:
|
|||
else:
|
||||
return llunit
|
||||
elif types._is_pointer(typ):
|
||||
return llptr
|
||||
return ll.PointerType(self.llty_of_type(typ["elt"]))
|
||||
elif types.is_function(typ):
|
||||
sretarg = []
|
||||
llretty = self.llty_of_type(typ.ret, for_return=True)
|
||||
|
@ -249,7 +249,7 @@ class LLVMIRGenerator:
|
|||
elif builtins.is_array(typ):
|
||||
llshapety = self.llty_of_type(typ.attributes["shape"])
|
||||
llbufferty = self.llty_of_type(typ.attributes["buffer"])
|
||||
return ll.LiteralStructType([llshapety, llbufferty])
|
||||
return ll.LiteralStructType([llbufferty, llshapety])
|
||||
elif builtins.is_listish(typ):
|
||||
lleltty = self.llty_of_type(builtins.get_iterable_elt(typ))
|
||||
return ll.LiteralStructType([lleltty.as_pointer(), lli32])
|
||||
|
@ -737,28 +737,13 @@ class LLVMIRGenerator:
|
|||
name=insn.name)
|
||||
else:
|
||||
assert False
|
||||
elif builtins.is_listish(insn.type) and not builtins.is_array(insn.type):
|
||||
if builtins.is_listish(insn.operands[0].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
|
||||
|
||||
elif types._is_pointer(insn.type) or (builtins.is_listish(insn.type)
|
||||
and not builtins.is_array(insn.type)):
|
||||
llsize = self.map(insn.operands[0])
|
||||
lleltty = self.llty_of_type(builtins.get_iterable_elt(insn.type))
|
||||
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 = self.llbuilder.insert_value(llvalue, llalloc, 0, name=insn.name)
|
||||
llvalue = self.llbuilder.insert_value(llvalue, llsize, 1)
|
||||
|
@ -962,20 +947,28 @@ class LLVMIRGenerator:
|
|||
inbounds=True, name=insn.name)
|
||||
return self.llbuilder.store(llvalue, llptr)
|
||||
|
||||
def process_GetElem(self, insn):
|
||||
lst, idx = insn.list(), insn.index()
|
||||
lllst, llidx = map(self.map, (lst, idx))
|
||||
llelts = self.llbuilder.extract_value(lllst, 0)
|
||||
def process_Offset(self, insn):
|
||||
base, idx = insn.base(), insn.index()
|
||||
llelts, llidx = map(self.map, (base, idx))
|
||||
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)
|
||||
return llelt
|
||||
|
||||
def process_GetElem(self, insn):
|
||||
llelt = self.process_Offset(insn)
|
||||
llvalue = self.llbuilder.load(llelt)
|
||||
if isinstance(llvalue.type, ll.PointerType):
|
||||
self.mark_dereferenceable(llvalue)
|
||||
return llvalue
|
||||
|
||||
def process_SetElem(self, insn):
|
||||
lst, idx = insn.list(), insn.index()
|
||||
lllst, llidx = map(self.map, (lst, idx))
|
||||
llelts = self.llbuilder.extract_value(lllst, 0)
|
||||
base, idx = insn.base(), insn.index()
|
||||
llelts, llidx = map(self.map, (base, idx))
|
||||
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)
|
||||
return self.llbuilder.store(self.map(insn.value()), llelt)
|
||||
|
||||
|
@ -1190,7 +1183,8 @@ class LLVMIRGenerator:
|
|||
collection, = insn.operands
|
||||
if builtins.is_array(collection.type):
|
||||
# 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(self.map(collection), 1)
|
||||
elif insn.op in ("printf", "rtio_log"):
|
||||
|
|
|
@ -204,8 +204,10 @@ class TTuple(Type):
|
|||
return hash(tuple(self.elts))
|
||||
|
||||
class _TPointer(TMono):
|
||||
def __init__(self):
|
||||
super().__init__("pointer")
|
||||
def __init__(self, elt=None):
|
||||
if elt is None:
|
||||
elt = TMono("int", {"width": 8}) # i8*
|
||||
super().__init__("pointer", params={"elt": elt})
|
||||
|
||||
class TFunction(Type):
|
||||
"""
|
||||
|
@ -735,6 +737,8 @@ class TypePrinter(object):
|
|||
else:
|
||||
return "%s(%s)" % (typ.name, ", ".join(
|
||||
["%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):
|
||||
if len(typ.elts) == 1:
|
||||
return "(%s,)" % self.name(typ.elts[0], depth + 1)
|
||||
|
|
Loading…
Reference in New Issue