From ec9d40b04f53dd5f36dc21d1a03d2e6ee8fda065 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 21 Jul 2015 13:45:27 +0300 Subject: [PATCH] Add LLVM IR generation for function calls. --- artiq/compiler/ir.py | 4 +- .../compiler/transforms/artiq_ir_generator.py | 6 +- .../compiler/transforms/llvm_ir_generator.py | 76 +++++++++++++------ 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/artiq/compiler/ir.py b/artiq/compiler/ir.py index 9d55cb069..55313b7ed 100644 --- a/artiq/compiler/ir.py +++ b/artiq/compiler/ir.py @@ -467,7 +467,7 @@ class TEnvironment(types.TMono): def is_environment(typ): return isinstance(typ, TEnvironment) -class EnvironmentArgument(NamedValue): +class EnvironmentArgument(Argument): """ A function argument specifying an outer environment. """ @@ -799,7 +799,7 @@ class Call(Instruction): def opcode(self): return "call" - def function(self): + def target_function(self): return self.operands[0] def arguments(self): diff --git a/artiq/compiler/transforms/artiq_ir_generator.py b/artiq/compiler/transforms/artiq_ir_generator.py index 77f90b336..f449b4a2f 100644 --- a/artiq/compiler/transforms/artiq_ir_generator.py +++ b/artiq/compiler/transforms/artiq_ir_generator.py @@ -174,7 +174,7 @@ class ARTIQIRGenerator(algorithm.Visitor): optargs = [] for arg_name in typ.optargs: - optargs.append(ir.Argument(ir.TSSAOption(typ.optargs[arg_name]), "arg." + arg_name)) + optargs.append(ir.Argument(ir.TOption(typ.optargs[arg_name]), "arg." + arg_name)) func = ir.Function(typ, ".".join(self.name), [env_arg] + args + optargs) self.functions.append(func) @@ -1189,11 +1189,11 @@ class ARTIQIRGenerator(algorithm.Visitor): optarg_typ = ir.TOption(typ.optargs[optarg_name]) for keyword in node.keywords: if keyword.arg == optarg_name: - value = self.append(ir.Alloc(optarg_typ, [self.visit(keyword.value)])) + value = self.append(ir.Alloc([self.visit(keyword.value)], optarg_typ)) args.append(value) break else: - value = self.append(ir.Alloc(optarg_typ, [])) + value = self.append(ir.Alloc([], optarg_typ)) args.append(value) if self.unwind_target is None: diff --git a/artiq/compiler/transforms/llvm_ir_generator.py b/artiq/compiler/transforms/llvm_ir_generator.py index 416c87a1f..dcbcb3350 100644 --- a/artiq/compiler/transforms/llvm_ir_generator.py +++ b/artiq/compiler/transforms/llvm_ir_generator.py @@ -16,18 +16,21 @@ class LLVMIRGenerator: self.llmap = {} self.fixups = [] - def llty_of_type(self, typ, for_alloc=False, for_return=False): + 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]) elif types.is_function(typ): - envarg = ll.IntType(8).as_pointer + envarg = ll.IntType(8).as_pointer() llty = ll.FunctionType(args=[envarg] + [self.llty_of_type(typ.args[arg]) for arg in typ.args] + [self.llty_of_type(ir.TOption(typ.optargs[arg])) for arg in typ.optargs], return_type=self.llty_of_type(typ.ret, for_return=True)) - return llty.as_pointer() + if bare: + return llty + else: + return ll.LiteralStructType([envarg, llty.as_pointer()]) elif builtins.is_none(typ): if for_return: return ll.VoidType() @@ -55,7 +58,7 @@ class LLVMIRGenerator: elif ir.is_environment(typ): llty = ll.LiteralStructType([self.llty_of_type(typ.params[name]) for name in typ.params]) - if for_alloc: + if bare: return llty else: return llty.as_pointer() @@ -76,10 +79,17 @@ class LLVMIRGenerator: assert False def map(self, value): - if isinstance(value, (ir.Instruction, ir.BasicBlock)): + if isinstance(value, (ir.Argument, ir.Instruction, ir.BasicBlock)): return self.llmap[value] elif isinstance(value, ir.Constant): return self.llconst_of_const(value) + elif isinstance(value, ir.Function): + llfun = self.llmodule.get_global(value.name) + if llfun is None: + return ll.Function(self.llmodule, self.llty_of_type(value.type, bare=True), + value.name) + else: + return llfun else: assert False @@ -88,24 +98,30 @@ class LLVMIRGenerator: self.process_function(func) def process_function(self, func): - llargtys = [] - for arg in func.arguments: - llargtys.append(self.llty_of_type(arg.type)) - llfunty = ll.FunctionType(args=llargtys, - return_type=self.llty_of_type(func.type.ret, for_return=True)) - try: - self.llfunction = ll.Function(self.llmodule, llfunty, func.name) + self.llfunction = self.llmodule.get_global(func.name) + if self.llfunction is None: + llargtys = [] + for arg in func.arguments: + llargtys.append(self.llty_of_type(arg.type)) + llfunty = ll.FunctionType(args=llargtys, + return_type=self.llty_of_type(func.type.ret, for_return=True)) + self.llfunction = ll.Function(self.llmodule, llfunty, func.name) + self.llmap = {} self.llbuilder = ll.IRBuilder() self.fixups = [] - # First, create all basic blocks. + # First, map arguments. + for arg, llarg in zip(func.arguments, self.llfunction.args): + self.llmap[arg] = llarg + + # Second, create all basic blocks. for block in func.basic_blocks: llblock = self.llfunction.append_basic_block(block.name) self.llmap[block] = llblock - # Second, translate all instructions. + # Third, translate all instructions. for block in func.basic_blocks: self.llbuilder.position_at_end(self.llmap[block]) for insn in block.instructions: @@ -113,7 +129,7 @@ class LLVMIRGenerator: assert llinsn is not None self.llmap[insn] = llinsn - # Third, fixup phis. + # Fourth, fixup phis. for fixup in self.fixups: fixup() finally: @@ -131,7 +147,7 @@ class LLVMIRGenerator: def process_Alloc(self, insn): if ir.is_environment(insn.type): - return self.llbuilder.alloca(self.llty_of_type(insn.type, for_alloc=True), + return self.llbuilder.alloca(self.llty_of_type(insn.type, bare=True), name=insn.name) elif builtins.is_list(insn.type): llsize = self.map(insn.operands[0]) @@ -171,7 +187,14 @@ class LLVMIRGenerator: def process_SetLocal(self, insn): env = insn.environment() llptr = self.llptr_to_var(self.map(env), env.type, insn.var_name) - return self.llbuilder.store(self.map(insn.value()), llptr) + llvalue = self.map(insn.value()) + if llptr.type.pointee != llvalue.type: + # The environment argument is an i8*, so that all closures can + # unify with each other regardless of environment type or size. + # We fixup the type on assignment into the ".outer" slot. + assert isinstance(insn.value(), ir.EnvironmentArgument) + llvalue = self.llbuilder.bitcast(llvalue, llptr.type.pointee) + return self.llbuilder.store(llvalue, llptr) def attr_index(self, insn): return list(insn.object().type.attributes.keys()).index(insn.attr) @@ -362,11 +385,20 @@ class LLVMIRGenerator: else: assert False - # def process_Closure(self, insn): - # pass + def process_Closure(self, insn): + llvalue = ll.Constant(self.llty_of_type(insn.target_function.type), ll.Undefined) + llenv = self.llbuilder.bitcast(self.map(insn.environment()), ll.IntType(8).as_pointer()) + llvalue = self.llbuilder.insert_value(llvalue, llenv, 0) + llvalue = self.llbuilder.insert_value(llvalue, self.map(insn.target_function), 1, + name=insn.name) + return llvalue - # def process_Call(self, insn): - # pass + def process_Call(self, insn): + llclosure, llargs = self.map(insn.target_function()), map(self.map, insn.arguments()) + llenv = self.llbuilder.extract_value(llclosure, 0) + llfun = self.llbuilder.extract_value(llclosure, 1) + return self.llbuilder.call(llfun, [llenv] + list(llargs), + name=insn.name) def process_Select(self, insn): return self.llbuilder.select(self.map(insn.cond()), @@ -383,7 +415,7 @@ class LLVMIRGenerator: # pass def process_Return(self, insn): - if builtins.is_none(insn.type): + if builtins.is_none(insn.value().type): return self.llbuilder.ret_void() else: return self.llbuilder.ret(self.llmap[insn.value()])