def make_branch_func_object() -> Tuple[Function, Tuple[BasicBlock, ...]]: bb0 = BasicBlock("bb0") bb1 = BasicBlock("bb1") bb2 = BasicBlock("bb2") bb3 = BasicBlock("bb3") bb0.add_inst(bytecode.CopyInst(Var('v0'), NumLit(SNum(42)))) bb0.add_inst(bytecode.BrInst(Var('x'), bb1)) bb0.add_inst(bytecode.JmpInst(bb2)) bb1.add_inst( bytecode.BinopInst(Var('v0'), Binop.ADD, Var('v0'), NumLit(SNum(27)))) bb1.add_inst(bytecode.JmpInst(bb3)) bb2.add_inst(bytecode.CopyInst(Var('v0'), SymLit(SSym('hi')))) bb2.add_inst(bytecode.JmpInst(bb3)) bb3.add_inst(bytecode.ReturnInst(Var('v0'))) return Function([Var('x')], bb0), (bb0, bb1, bb2, bb3)
def make_func() -> Function: bb0 = BasicBlock("bb0") bb1 = BasicBlock("bb1") bb0.add_inst(bytecode.CopyInst(Var('v0'), NumLit(SNum(42)))) bb0.add_inst( bytecode.BinopInst(Var('v1'), Binop.ADD, Var('v0'), NumLit(SNum(69)))) bb0.add_inst(bytecode.JmpInst(bb1)) bb1.add_inst(bytecode.ReturnInst(Var('v1'))) return Function([], bb0)
def visit_SCall(self, call: sexp.SCall) -> None: assert not self.quoted, 'Non-primitives in quoted list unsupported' if self._is_true_assertion(call): self.result = bytecode.NumLit(sexp.SNum(0)) return tail_call_data = self._get_tail_call_data(call) is_known_function = self._is_known_function(call) arity_known_correct = self._arity_known_correct( call, is_known_function, tail_call_data) tail_call_args_compatible = self._tail_call_args_compatible( call, tail_call_data) func_to_call = self._get_func_to_call( call, is_known_function, arity_known_correct, tail_call_data, tail_call_args_compatible) args = self._emit_args(call) if tail_call_data is not None and func_to_call is None: for arg, param in zip(args, tail_call_data.func_params): local_var = self.local_env[param] if arg != local_var: self.end_block.add_inst(bytecode.CopyInst(local_var, arg)) assert self._function_entrypoint is not None self.end_block.add_inst( bytecode.JmpInst(self._function_entrypoint)) # We need a placeholder result since we're jumping back # to the beginning of the function self.result = bytecode.NumLit(sexp.SNum(0)) else: assert func_to_call is not None call_result_var = bytecode.Var(next(self.var_names)) call_instr = bytecode.CallInst( call_result_var, func_to_call, args, specialization=self._get_arg_types(call)) self.end_block.add_inst(call_instr) self.result = call_result_var
def test_example_tail_call(self) -> None: """ function list? (v0) entry=bb0 bb0: v1 = lookup 'nil? v2 = call v1 (v0) br v2 bb1 v3 = lookup 'pair? v4 = call v3 (v0) brn v4 bb2 v5 = lookup 'cdr v0 = call v5 (v0) jmp bb0 bb1: return 'true bb2: return 'false """ bb0 = bytecode.BasicBlock("bb0") bb1 = bytecode.BasicBlock("bb1") bb2 = bytecode.BasicBlock("bb2") is_list = bytecode.Function([Var("v0")], bb0) bb0.add_inst(bytecode.LookupInst(Var("v1"), SymLit(SSym("nil?")))) bb0.add_inst(bytecode.CallInst(Var("v2"), Var("v1"), [Var("v0")])) bb0.add_inst(bytecode.BrInst(Var("v2"), bb1)) bb0.add_inst(bytecode.LookupInst(Var("v3"), SymLit(SSym("pair?")))) bb0.add_inst(bytecode.CallInst(Var("v4"), Var("v3"), [Var("v0")])) bb0.add_inst(bytecode.BrnInst(Var("v4"), bb2)) bb0.add_inst(bytecode.LookupInst(Var("v5"), SymLit(SSym("global")))) bb0.add_inst(bytecode.CallInst(Var("v0"), Var("v5"), [Var("v0")])) bb0.add_inst(bytecode.JmpInst(bb0)) bb1.add_inst(bytecode.ReturnInst(SymLit(SSym("true")))) bb2.add_inst(bytecode.ReturnInst(SymLit(SSym("false")))) assert is_list
def visit_SConditional(self, conditional: sexp.SConditional) -> None: assert not self.quoted, 'Non-primitives in quoted list unsupported' if (self._expr_types is not None and self._expr_types.expr_value_known(conditional.test)): test_val = self._expr_types.get_expr_value(conditional.test) assert isinstance(test_val, sexp.SBool) branch_to_emit = (conditional.then_expr if test_val.value else conditional.else_expr) emitter = ExpressionEmitter( self.parent_block, self.bb_names, self.var_names, self.local_env, self.global_env, function_entrypoint=self._function_entrypoint, tail_calls=self._tail_calls, expr_types=self._expr_types) emitter.visit(branch_to_emit) self.result = emitter.result self.end_block = emitter.end_block return test_emitter = ExpressionEmitter( self.parent_block, self.bb_names, self.var_names, self.local_env, self.global_env, function_entrypoint=self._function_entrypoint, tail_calls=self._tail_calls, expr_types=self._expr_types) test_emitter.visit(conditional.test) then_block = bytecode.BasicBlock(next(self.bb_names)) else_block = bytecode.BasicBlock(next(self.bb_names)) result_var = bytecode.Var(next(self.var_names)) then_br_instr = bytecode.BrInst(test_emitter.result, then_block) else_br_instr = bytecode.JmpInst(else_block) test_emitter.end_block.add_inst(then_br_instr) test_emitter.end_block.add_inst(else_br_instr) then_emitter = ExpressionEmitter( then_block, self.bb_names, self.var_names, self.local_env, self.global_env, function_entrypoint=self._function_entrypoint, tail_calls=self._tail_calls, expr_types=self._expr_types) then_emitter.visit(conditional.then_expr) then_result_instr = bytecode.CopyInst(result_var, then_emitter.result) then_emitter.end_block.add_inst(then_result_instr) else_emitter = ExpressionEmitter( else_block, self.bb_names, self.var_names, self.local_env, self.global_env, function_entrypoint=self._function_entrypoint, tail_calls=self._tail_calls, expr_types=self._expr_types) else_emitter.visit(conditional.else_expr) else_result_instr = bytecode.CopyInst(result_var, else_emitter.result) else_emitter.end_block.add_inst(else_result_instr) new_end_block = bytecode.BasicBlock(next(self.bb_names)) then_emitter.end_block.add_inst(bytecode.JmpInst(new_end_block)) else_emitter.end_block.add_inst(bytecode.JmpInst(new_end_block)) self.end_block = new_end_block self.result = result_var
def test_example_inlined(self) -> None: """ function list? (v0) entry=bb0 bb0: v1 = typeof v0 v2 = sym_eq v1 'vector brn v2 false v3 = length v0 v4 = num_eq v3 0 br v4 true v5 = num_eq v3 2 brn v5 false v0 = load v0 [1] jmp bb0 true: return 'true false: return 'false """ bb0 = bytecode.BasicBlock("bb0") true = bytecode.BasicBlock("true") false = bytecode.BasicBlock("false") is_list = bytecode.Function([Var("v0")], bb0) # These tests are just to test the API bb0.add_inst(bytecode.TypeofInst(Var("v1"), Var("v0"))) bb0.add_inst( bytecode.BinopInst(Var("v2"), Binop.SYM_EQ, Var("v1"), SymLit(SSym("vector")))) bb0.add_inst(bytecode.BrnInst(Var("v2"), false)) bb0.add_inst(bytecode.LengthInst(Var("v3"), Var("v0"))) bb0.add_inst( bytecode.BinopInst(Var("v4"), Binop.NUM_EQ, Var("v3"), NumLit(SNum(0)))) bb0.add_inst(bytecode.BrInst(Var("v4"), true)) bb0.add_inst( bytecode.BinopInst(Var("v5"), Binop.NUM_EQ, Var("v3"), NumLit(SNum(2)))) bb0.add_inst(bytecode.BrnInst(Var("v5"), false)) bb0.add_inst(bytecode.LoadInst(Var("v0"), Var("v0"), NumLit(SNum(1)))) bb0.add_inst(bytecode.JmpInst(bb0)) true.add_inst(bytecode.ReturnInst(BoolLit(SBool(True)))) false.add_inst(bytecode.ReturnInst(BoolLit(SBool(False)))) env = bytecode.EvalEnv(local_env={Var("v0"): SNum(42)}) gen = bytecode.ResultGenerator(is_list.run(env)) gen.run() self.assertEqual(gen.value, SBool(False)) env = bytecode.EvalEnv(local_env={ Var("v0"): SVect([SNum(42), SVect([SNum(69), SVect([])])]) }) gen = bytecode.ResultGenerator(is_list.run(env)) gen.run() self.assertEqual(gen.value, SBool(True))