def test_lambda(self) -> None: prog = '(lambda (spam egg) (+ spam egg)) (lambda () 42)' self.assertEqual([ SFunction( SSym('__lambda0'), [SSym('spam'), SSym('egg')], sexp.to_slist([SCall(SSym('+'), [SSym('spam'), SSym('egg')])]), is_lambda=True), SFunction(SSym('__lambda1'), [], sexp.to_slist([SNum(42)]), is_lambda=True), ], sexp.parse(prog))
def _generate_specialization(self, env: EvalEnv, func: sexp.SFunction, func_code: Function, type_tuple: TypeTuple) -> None: if not (env.jit or env.bytecode_jit): return if env.print_specializations: type_names = ', '.join(str(s) for s in type_tuple) print(f"Specializing: {func.name} ({type_names})") type_analyzer = None if env.jit: param_types = dict(zip(func.params, type_tuple)) type_analyzer = scheme_types.FunctionTypeAnalyzer( param_types, env._global_env) type_analyzer.visit(func) tail_calls = None if env.optimize_tail_calls: tail_call_finder = find_tail_calls.TailCallFinder() tail_call_finder.visit(func) tail_calls = tail_call_finder.tail_calls from emit_IR import FunctionEmitter emitter = FunctionEmitter(env._global_env, tail_calls=tail_calls, expr_types=type_analyzer) emitter.visit(func) emitted_func = emitter.get_emitted_func() func.specializations[type_tuple] = emitted_func if env.bytecode_jit: self._optimize(env, func, type_tuple)
def _optimize(self, env: EvalEnv, func: sexp.SFunction, type_tuple: TypeTuple) -> None: from optimization import FunctionOptimizer if env.print_optimizations: type_names = ', '.join(str(s) for s in type_tuple) print(f"Optimizing {func.name} ({type_names})") opt = FunctionOptimizer(func.get_specialized(type_tuple)) opt.specialization = type_tuple opt.optimize(env)
def test_function_def(self) -> None: prog = '(define (funcy spam egg) (+ spam egg)) (funcy 42 43)' self.assertEqual([ SFunction( SSym('funcy'), [SSym('spam'), SSym('egg')], sexp.to_slist([SCall(SSym('+'), [SSym('spam'), SSym('egg')])])), SCall(SSym('funcy'), [SNum(42), SNum(43)]) ], sexp.parse(prog))
def should_inline(self, env: EvalEnv, func: SFunction, types: Optional[TypeTuple]) -> bool: if func.name in self.banned_from_inline: return False name = func.name.name if name.startswith('inst/'): return True ALWAYS_INLINE = ( 'trap', 'trace', 'breakpoint', 'assert', 'typeof', 'number?', 'symbol?', 'vector?', 'function?', 'bool?', ) if name in ALWAYS_INLINE: return True if not types: return False SHOULD_INLINE = ( 'pair?', 'nil?', 'symbol=', '+', '-', '*', '/', '%', 'pointer=', 'number=', 'number<', 'vector-length', 'vector-index', 'vector-set!', '<', '!=', '>', '<=', '>=', 'cons', 'car', 'cdr', ) if not any(t == SchemeObject for t in types) and name in SHOULD_INLINE: return True spec = func.get_specialized(types) icount = sum(len(b.instructions) for b in spec.blocks()) return icount <= env.inline_threshold
def test_lambda_called_inline(self) -> None: self.maxDiff = None prog = '((lambda (spam egg) (+ spam egg)) 42 43)' self.assertEqual([ SCall( SFunction(SSym('__lambda0'), [SSym('spam'), SSym('egg')], sexp.to_slist( [SCall(SSym('+'), [SSym('spam'), SSym('egg')])]), is_lambda=True), [SNum(42), SNum(43)]) ], sexp.parse(prog))
def inst_function( name: SSym, params: List[Var], return_val: Optional[bytecode.Parameter], *insts: Inst, ) -> SFunction: """Create a function out of the instructions in insts.""" begin = BasicBlock('bb0') for inst in insts: begin.add_inst(inst) if return_val is not None: begin.add_inst(bytecode.ReturnInst(return_val)) code = Function(params, begin) param_syms = [SSym(p.name) for p in params] return SFunction(name, param_syms, Nil, code, False)
def test_comments(self) -> None: prog = """ ;;; We want to define a cool function here! (define ; hi ;; A function name (cool-func x y) ; wow ;; branches are cheaper than subtraction, right? :P (if (= x y) 0 (- x y))) """ self.assertEqual([ SFunction( SSym('cool-func'), [SSym('x'), SSym('y')], sexp.to_slist([ SConditional( SCall(SSym('='), [SSym('x'), SSym('y')]), SNum(0), SCall(SSym('-'), [SSym('x'), SSym('y')])) ])) ], sexp.parse(prog))
def visit_SFunction(self, func: sexp.SFunction) -> None: assert func.is_lambda, 'Nested named functions not supported' assert not self.quoted, 'Non-primitives in quoted list unsupported' # Don't re-emit lambdas defined in a function we're specializing func_emitter = FunctionEmitter(self.global_env) func_emitter.visit(func) func.code = func_emitter.get_emitted_func() if func.name in self.global_env: looked_up_func = self.global_env[func.name] if isinstance(looked_up_func, sexp.SFunction): assert func.code == looked_up_func.code else: assert False, f"Duplicate function name: {func.name}" else: self.global_env[func.name] = func lambda_var = bytecode.Var(next(self.var_names)) lookup_lambda_instr = bytecode.LookupInst( lambda_var, bytecode.SymLit(func.name)) self.parent_block.add_inst(lookup_lambda_instr) self.result = lambda_var
def _add_func_to_env(func: sexp.SFunction, func_emitter: FunctionEmitter, env: EvalEnv) -> None: func.code = func_emitter.get_emitted_func() assert func.name not in env._global_env, ( f"Duplicate function name: {func.name}") env._global_env[func.name] = func