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})
|
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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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"):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue