diff --git a/artiq/compiler/builtins.py b/artiq/compiler/builtins.py index 8ca095b81..4b914063f 100644 --- a/artiq/compiler/builtins.py +++ b/artiq/compiler/builtins.py @@ -50,6 +50,10 @@ class TFloat(types.TMono): def one(): return 1.0 +class TStr(types.TMono): + def __init__(self): + super().__init__("str") + class TList(types.TMono): def __init__(self, elt=None): if elt is None: @@ -88,6 +92,9 @@ def fn_int(): def fn_float(): return types.TConstructor("float") +def fn_str(): + return types.TConstructor("str") + def fn_list(): return types.TConstructor("list") @@ -133,6 +140,9 @@ def get_int_width(typ): def is_float(typ): return types.is_mono(typ, "float") +def is_str(typ): + return types.is_mono(typ, "str") + def is_numeric(typ): typ = typ.find() return isinstance(typ, types.TMono) and \ @@ -167,6 +177,6 @@ def is_collection(typ): return isinstance(typ, types.TTuple) or \ types.is_mono(typ, "list") -def is_mutable(typ): +def is_allocated(typ): return typ.fold(False, lambda accum, typ: - is_list(typ) or types.is_function(typ)) + is_list(typ) or is_str(typ) or types.is_function(typ)) diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index f449b4a2f..c18b987a2 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -565,6 +565,9 @@ class ARTIQIRGenerator(algorithm.Visitor): def visit_NumT(self, node): return ir.Constant(node.n, node.type) + def visit_StrT(self, node): + return ir.Constant(node.s, node.type) + def visit_NameConstantT(self, node): return ir.Constant(node.value, node.type) @@ -1038,7 +1041,7 @@ class ARTIQIRGenerator(algorithm.Visitor): return result def _compare_pair_identity(self, op, lhs, rhs): - if builtins.is_mutable(lhs) and builtins.is_mutable(rhs): + if builtins.is_allocated(lhs) and builtins.is_allocated(rhs): # These are actually pointers, compare directly. return self.append(ir.Compare(op, lhs, rhs)) else: diff --git a/artiq/compiler/transforms/asttyped_rewriter.py b/artiq/compiler/transforms/asttyped_rewriter.py index 0c353bee6..df0b46d91 100644 --- a/artiq/compiler/transforms/asttyped_rewriter.py +++ b/artiq/compiler/transforms/asttyped_rewriter.py @@ -252,6 +252,11 @@ class ASTTypedRewriter(algorithm.Transformer): return asttyped.NumT(type=typ, n=node.n, loc=node.loc) + def visit_Str(self, node): + return asttyped.StrT(type=builtins.TStr(), + s=node.s, + begin_loc=node.begin_loc, end_loc=node.end_loc, loc=node.loc) + def visit_Name(self, node): return asttyped.NameT(type=self._find_name(node.id, node.loc), id=node.id, ctx=node.ctx, loc=node.loc) @@ -411,7 +416,6 @@ class ASTTypedRewriter(algorithm.Transformer): visit_GeneratorExp = visit_unsupported visit_Set = visit_unsupported visit_SetComp = visit_unsupported - visit_Str = visit_unsupported visit_Starred = visit_unsupported visit_Yield = visit_unsupported visit_YieldFrom = visit_unsupported diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index dcbcb3350..717c2f169 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -18,7 +18,7 @@ class LLVMIRGenerator: def llty_of_type(self, typ, bare=False, for_return=False): if types.is_tuple(typ): - return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.elts]) + return ll.LiteralStructType([self.llty_of_type(eltty) for eltty in typ.find().elts]) elif types.is_function(typ): envarg = ll.IntType(8).as_pointer() llty = ll.FunctionType(args=[envarg] + @@ -42,6 +42,8 @@ class LLVMIRGenerator: return ll.IntType(builtins.get_int_width(typ)) elif builtins.is_float(typ): return ll.DoubleType() + elif builtins.is_str(typ): + return ll.IntType(8).as_pointer() elif builtins.is_list(typ): lleltty = self.llty_of_type(builtins.get_iterable_elt(typ)) return ll.LiteralStructType([ll.IntType(32), lleltty.as_pointer()]) @@ -75,6 +77,14 @@ class LLVMIRGenerator: return ll.Constant(llty, False) elif isinstance(const.value, (int, float)): return ll.Constant(llty, const.value) + elif isinstance(const.value, str): + as_bytes = const.value.encode('utf-8') + llstrty = ll.ArrayType(ll.IntType(8), len(as_bytes)) + llconst = ll.GlobalVariable(self.llmodule, llstrty, + name=self.llmodule.get_unique_name("str")) + llconst.global_constant = True + llconst.initializer = ll.Constant(llstrty, bytearray(as_bytes)) + return llconst.bitcast(ll.IntType(8).as_pointer()) else: assert False @@ -157,7 +167,7 @@ class LLVMIRGenerator: size=llsize) llvalue = self.llbuilder.insert_value(llvalue, llalloc, 1, name=insn.name) return llvalue - elif builtins.is_mutable(insn.type): + elif builtins.is_allocated(insn.type): assert False else: # immutable llvalue = ll.Constant(self.llty_of_type(insn.type), ll.Undefined) @@ -203,7 +213,7 @@ class LLVMIRGenerator: if types.is_tuple(insn.object().type): return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn), name=insn.name) - elif not builtins.is_mutable(insn.object().type): + elif not builtins.is_allocated(insn.object().type): return self.llbuilder.extract_value(self.map(insn.object()), self.attr_index(insn), name=insn.name) else: @@ -213,7 +223,7 @@ class LLVMIRGenerator: return self.llbuilder.load(llptr) def process_SetAttr(self, insn): - assert builtins.is_mutable(insns.object().type) + assert builtins.is_allocated(insns.object().type) llptr = self.llbuilder.gep(self.map(insn.object()), [self.llindex(0), self.llindex(self.attr_index(insn))], name=insn.name) diff --git a/artiq/compiler/validators/escape.py b/artiq/compiler/validators/escape.py index fd030eeef..603b97d7d 100644 --- a/artiq/compiler/validators/escape.py +++ b/artiq/compiler/validators/escape.py @@ -78,7 +78,7 @@ class RegionOf(algorithm.Visitor): # Value lives as long as the current scope, if it's mutable, # or else forever def visit_BinOpT(self, node): - if builtins.is_mutable(node.type): + if builtins.is_allocated(node.type): return self.youngest_region else: return None @@ -86,7 +86,7 @@ class RegionOf(algorithm.Visitor): # Value lives as long as the object/container, if it's mutable, # or else forever def visit_accessor(self, node): - if builtins.is_mutable(node.type): + if builtins.is_allocated(node.type): return self.visit(node.value) else: return None @@ -125,20 +125,23 @@ class RegionOf(algorithm.Visitor): visit_ListCompT = visit_allocating visit_SetT = visit_allocating visit_SetCompT = visit_allocating - visit_StrT = visit_allocating # Value lives forever def visit_immutable(self, node): - assert not builtins.is_mutable(node.type) + assert not builtins.is_allocated(node.type) return None - visit_CompareT = visit_immutable - visit_EllipsisT = visit_immutable visit_NameConstantT = visit_immutable visit_NumT = visit_immutable + visit_EllipsisT = visit_immutable visit_UnaryOpT = visit_immutable + visit_CompareT = visit_immutable visit_CallT = visit_immutable + # Value is mutable, but still lives forever + def visit_StrT(self, node): + return None + # Not implemented def visit_unimplemented(self, node): assert False @@ -212,7 +215,7 @@ class EscapeValidator(algorithm.Visitor): self.youngest_env = {} for name in node.typing_env: - if builtins.is_mutable(node.typing_env[name]): + if builtins.is_allocated(node.typing_env[name]): self.youngest_env[name] = Region(None) # not yet known else: self.youngest_env[name] = None # lives forever @@ -277,7 +280,7 @@ class EscapeValidator(algorithm.Visitor): self.visit_assignment(target, node.value) def visit_AugAssign(self, node): - if builtins.is_mutable(node.target.type): + if builtins.is_allocated(node.target.type): # If the target is mutable, op-assignment will allocate # in the youngest region. self.visit_assignment(node.target, node.value, is_aug_assign=True) @@ -295,7 +298,7 @@ class EscapeValidator(algorithm.Visitor): self.engine.process(diag) def visit_Raise(self, node): - if builtins.is_mutable(node.exc.type): + if builtins.is_allocated(node.exc.type): note = diagnostic.Diagnostic("note", "this expression has type {type}", {"type": types.TypePrinter().name(node.exc.type)}, diff --git a/lit-test/compiler/inferencer/unify.py b/lit-test/compiler/inferencer/unify.py index 04c8aecbd..48681fdcb 100644 --- a/lit-test/compiler/inferencer/unify.py +++ b/lit-test/compiler/inferencer/unify.py @@ -54,3 +54,5 @@ not 1 lambda x, y=1: x # CHECK-L: lambda x:'k, y:int(width='l)=1:int(width='l): x:'k:(x:'k, ?y:int(width='l))->'k +k = "x" +# CHECK-L: k:str