Skip to content

Commit 1a518ea

Browse files
author
whitequark
committedAug 8, 2016
compiler.embedding: implement string concatenation.
Fixes #526.
1 parent 5a2306a commit 1a518ea

File tree

8 files changed

+85
-13
lines changed

8 files changed

+85
-13
lines changed
 

Diff for: ‎artiq/compiler/builtins.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ def is_array(typ, elt=None):
260260
return types.is_mono(typ, "array")
261261

262262
def is_listish(typ, elt=None):
263-
return is_list(typ, elt) or is_array(typ, elt)
263+
return is_list(typ, elt) or is_array(typ, elt) or (elt is None and is_str(typ))
264264

265265
def is_range(typ, elt=None):
266266
if elt is not None:
@@ -283,6 +283,10 @@ def is_iterable(typ):
283283
def get_iterable_elt(typ):
284284
if is_iterable(typ):
285285
return typ.find()["elt"].find()
286+
elif is_str(typ):
287+
return TInt(types.TValue(8))
288+
else:
289+
assert False
286290

287291
def is_collection(typ):
288292
typ = typ.find()

Diff for: ‎artiq/compiler/transforms/artiq_ir_generator.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,12 @@ def visit_While(self, node):
478478

479479
def iterable_len(self, value, typ=_size_type):
480480
if builtins.is_listish(value.type):
481+
if isinstance(value, ir.Constant):
482+
name = None
483+
else:
484+
name = "{}.len".format(value.name)
481485
return self.append(ir.Builtin("len", [value], typ,
482-
name="{}.len".format(value.name)))
486+
name=name))
483487
elif builtins.is_range(value.type):
484488
start = self.append(ir.GetAttr(value, "start"))
485489
stop = self.append(ir.GetAttr(value, "stop"))
@@ -1313,7 +1317,7 @@ def visit_BinOpT(self, node):
13131317
loc=node.right.loc)
13141318

13151319
return self.append(ir.Arith(node.op, lhs, rhs))
1316-
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple
1320+
elif isinstance(node.op, ast.Add): # list + list, tuple + tuple, str + str
13171321
lhs, rhs = self.visit(node.left), self.visit(node.right)
13181322
if types.is_tuple(node.left.type) and types.is_tuple(node.right.type):
13191323
elts = []
@@ -1327,6 +1331,10 @@ def visit_BinOpT(self, node):
13271331
rhs_length = self.iterable_len(rhs)
13281332

13291333
result_length = self.append(ir.Arith(ast.Add(loc=None), lhs_length, rhs_length))
1334+
if builtins.is_str(node.left.type):
1335+
result_last = result_length
1336+
result_length = self.append(ir.Arith(ast.Add(loc=None), result_length,
1337+
ir.Constant(1, self._size_type)))
13301338
result = self.append(ir.Alloc([result_length], node.type))
13311339

13321340
# Copy lhs
@@ -1350,6 +1358,10 @@ def body_gen(index):
13501358
lambda index: self.append(ir.Compare(ast.Lt(loc=None), index, rhs_length)),
13511359
body_gen)
13521360

1361+
if builtins.is_str(node.left.type):
1362+
self.append(ir.SetElem(result, result_last,
1363+
ir.Constant(0, builtins.TInt(types.TValue(8)))))
1364+
13531365
return result
13541366
else:
13551367
assert False

Diff for: ‎artiq/compiler/transforms/inferencer.py

+4
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ def _coerce_binop(self, op, left, right):
394394
self._unify(left.type, right.type,
395395
left.loc, right.loc)
396396
return left.type, left.type, right.type
397+
elif builtins.is_str(left.type) or builtins.is_str(right.type):
398+
self._unify(left.type, right.type,
399+
left.loc, right.loc)
400+
return left.type, left.type, right.type
397401
else:
398402
return self._coerce_numeric((left, right), lambda typ: (typ, typ, typ))
399403
elif isinstance(op, ast.Mult):

Diff for: ‎artiq/compiler/transforms/llvm_ir_generator.py

+27-9
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@ def llbuiltin(self, name):
344344
llty = ll.FunctionType(llvoid, [self.llty_of_type(builtins.TException())])
345345
elif name == "__artiq_reraise":
346346
llty = ll.FunctionType(llvoid, [])
347+
elif name == "strlen":
348+
llty = ll.FunctionType(lli32, [llptr])
347349
elif name == "strcmp":
348350
llty = ll.FunctionType(lli32, [llptr, llptr])
349351
elif name == "send_rpc":
@@ -610,6 +612,10 @@ def process_Alloc(self, insn):
610612
name=insn.name)
611613
else:
612614
assert False
615+
elif builtins.is_str(insn.type):
616+
llsize = self.map(insn.operands[0])
617+
llvalue = self.llbuilder.alloca(lli8, size=llsize)
618+
return llvalue
613619
elif builtins.is_listish(insn.type):
614620
llsize = self.map(insn.operands[0])
615621
llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined)
@@ -818,18 +824,27 @@ def process_SetAttr(self, insn):
818824
return self.llbuilder.store(llvalue, llptr)
819825

820826
def process_GetElem(self, insn):
821-
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
822-
llelt = self.llbuilder.gep(llelts, [self.map(insn.index())],
823-
inbounds=True)
824-
llvalue = self.llbuilder.load(llelt)
827+
lst, idx = insn.list(), insn.index()
828+
lllst, llidx = map(self.map, (lst, idx))
829+
if builtins.is_str(lst.type):
830+
llelt = self.llbuilder.gep(lllst, [llidx], inbounds=True)
831+
llvalue = self.llbuilder.load(llelt)
832+
else:
833+
llelts = self.llbuilder.extract_value(lllst, 1)
834+
llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True)
835+
llvalue = self.llbuilder.load(llelt)
825836
if isinstance(llvalue.type, ll.PointerType):
826837
self.mark_dereferenceable(llvalue)
827838
return llvalue
828839

829840
def process_SetElem(self, insn):
830-
llelts = self.llbuilder.extract_value(self.map(insn.list()), 1)
831-
llelt = self.llbuilder.gep(llelts, [self.map(insn.index())],
832-
inbounds=True)
841+
lst, idx = insn.list(), insn.index()
842+
lllst, llidx = map(self.map, (lst, idx))
843+
if builtins.is_str(lst.type):
844+
llelt = self.llbuilder.gep(lllst, [llidx], inbounds=True)
845+
else:
846+
llelts = self.llbuilder.extract_value(lllst, 1)
847+
llelt = self.llbuilder.gep(llelts, [llidx], inbounds=True)
833848
return self.llbuilder.store(self.map(insn.value()), llelt)
834849

835850
def process_Coerce(self, insn):
@@ -1045,8 +1060,11 @@ def get_outer(llenv, env_ty):
10451060
env, = insn.operands
10461061
return get_outer(self.map(env), env.type)
10471062
elif insn.op == "len":
1048-
lst, = insn.operands
1049-
return self.llbuilder.extract_value(self.map(lst), 0)
1063+
collection, = insn.operands
1064+
if builtins.is_str(collection.type):
1065+
return self.llbuilder.call(self.llbuiltin("strlen"), [self.map(collection)])
1066+
else:
1067+
return self.llbuilder.extract_value(self.map(collection), 0)
10501068
elif insn.op in ("printf", "rtio_log"):
10511069
# We only get integers, floats, pointers and strings here.
10521070
llargs = map(self.map, insn.operands)

Diff for: ‎artiq/compiler/validators/escape.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,6 @@ def visit_Return(self, node):
351351
{"type": types.TypePrinter().name(node.value.type)},
352352
node.value.loc)
353353
diag = diagnostic.Diagnostic("error",
354-
"cannot return a mutable value that does not live forever", {},
354+
"cannot return an allocated value that does not live forever", {},
355355
node.value.loc, notes=self._diagnostics_for(region, node.value.loc) + [note])
356356
self.engine.process(diag)

Diff for: ‎artiq/test/lit/escape/const_string.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# RUN: %python -m artiq.compiler.testbench.embedding %s
2+
3+
from artiq.experiment import *
4+
5+
@kernel
6+
def foo():
7+
return "x"
8+
9+
@kernel
10+
def entrypoint():
11+
foo()

Diff for: ‎artiq/test/lit/escape/error_string.py

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# RUN: %python -m artiq.compiler.testbench.embedding +diag %s 2>%t
2+
# RUN: OutputCheck %s --file-to-check=%t
3+
4+
from artiq.experiment import *
5+
6+
@kernel
7+
def foo():
8+
# CHECK-NOT-L: ${LINE:+1}: error:
9+
return "x"
10+
11+
@kernel
12+
def bar():
13+
# CHECK-L: ${LINE:+1}: error: cannot return an allocated value that does not live forever
14+
return "x" + "y"
15+
16+
@kernel
17+
def entrypoint():
18+
foo()
19+
bar()

Diff for: ‎artiq/test/lit/integration/str.py

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# RUN: %python -m artiq.compiler.testbench.jit %s
2+
# RUN: %python %s
3+
4+
assert ("x" + "y") == "xy"

0 commit comments

Comments
 (0)
Please sign in to comment.