def visit_Quote(self, quote: sexp.Quote) -> None: if isinstance(quote.expr, sexp.SSym): self.quoted = True super().visit(quote.expr) return is_list = isinstance(quote.expr, sexp.SPair) and quote.expr.is_list assert quote.expr is sexp.Nil or is_list quoted_exprs = list(cast(sexp.SList, quote.expr)) nil_var = bytecode.Var(next(self.var_names)) nil_alloc = bytecode.AllocInst( nil_var, bytecode.NumLit(sexp.SNum(0))) cdr = nil_var self.parent_block.add_inst(nil_alloc) for expr in reversed(quoted_exprs): pair_var = bytecode.Var(next(self.var_names)) pair_alloc = bytecode.AllocInst( pair_var, bytecode.NumLit(sexp.SNum(2))) self.parent_block.add_inst(pair_alloc) expr_emitter = ExpressionEmitter( self.parent_block, self.bb_names, self.var_names, self.local_env, self.global_env, quoted=True, function_entrypoint=self._function_entrypoint, tail_calls=self._tail_calls, expr_types=self._expr_types) expr_emitter.visit(expr) store_car = bytecode.StoreInst( pair_var, bytecode.NumLit(sexp.SNum(0)), expr_emitter.result) self.parent_block.add_inst(store_car) store_cdr = bytecode.StoreInst( pair_var, bytecode.NumLit(sexp.SNum(1)), cdr ) self.parent_block.add_inst(store_cdr) cdr = pair_var self.result = cdr
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 _add_arity_check( self, function_expr: bytecode.Parameter, add_to_block: bytecode.BasicBlock, arity: int) -> None: arity_var = bytecode.Var(next(self.var_names)) add_to_block.add_inst(bytecode.ArityInst(arity_var, function_expr)) correct_arity_var = bytecode.Var(next(self.var_names)) add_to_block.add_inst(bytecode.BinopInst( correct_arity_var, bytecode.Binop.NUM_EQ, arity_var, bytecode.NumLit(sexp.SNum(arity)) )) add_to_block.add_inst(bytecode.BrnInst(correct_arity_var, ARITY_TRAP))
def visit_SVect(self, vect: sexp.SVect) -> None: var = bytecode.Var(next(self.var_names)) instr = bytecode.AllocInst( var, bytecode.NumLit(sexp.SNum(len(vect.items)))) self.parent_block.add_inst(instr) self.result = var parent_block = self.parent_block for (i, expr) in enumerate(vect.items): expr_emitter = ExpressionEmitter( 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) expr_emitter.visit(expr) parent_block = expr_emitter.end_block store = bytecode.StoreInst( var, bytecode.NumLit(sexp.SNum(i)), expr_emitter.result) parent_block.add_inst(store)
def add_intrinsics(eval_env: EvalEnv) -> None: """Add intrinsics to the environment.""" 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 binop(name: SSym, op: Binop) -> SFunction: return inst_function( name, [Var('a'), Var('b')], result, bytecode.BinopInst(result, op, Var('a'), Var('b'))) result = Var('result') env = eval_env._global_env env[SSym('inst/typeof')] = inst_function( SSym('inst/typeof'), [Var('x')], result, bytecode.TypeofInst(result, Var('x'))) env[SSym('inst/trap')] = inst_function( SSym('inst/trap'), [], None, bytecode.TrapInst("(trap)")) env[SSym('inst/trace')] = inst_function( SSym('inst/trace'), [Var('x')], Var('x'), bytecode.TraceInst(Var('x'))) env[SSym('inst/display')] = inst_function( SSym('inst/display'), [Var('x')], bytecode.NumLit(sexp.SNum(0)), bytecode.DisplayInst(Var('x'))) env[SSym('inst/newline')] = inst_function( SSym('inst/newline'), [], bytecode.NumLit(sexp.SNum(0)), bytecode.NewlineInst()) env[SSym('inst/breakpoint')] = inst_function( SSym('inst/breakpoint'), [], bytecode.NumLit(sexp.SNum(0)), bytecode.BreakpointInst()) # Memory operations env[SSym('inst/alloc')] = inst_function( SSym('inst/alloc'), [Var('n')], result, bytecode.AllocInst(result, Var('n'))) env[SSym('inst/load')] = inst_function( SSym('inst/load'), [Var('v'), Var('n')], result, bytecode.LoadInst(result, Var('v'), Var('n'))) env[SSym('inst/store')] = inst_function( SSym('inst/store'), [Var('v'), Var('n'), Var('x')], Var('v'), bytecode.StoreInst(Var('v'), Var('n'), Var('x'))) env[SSym('inst/length')] = inst_function( SSym('inst/length'), [Var('v')], result, bytecode.LengthInst(result, Var('v'))) # Binary operators env[SSym('inst/+')] = binop(SSym('inst/+'), Binop.ADD) env[SSym('inst/-')] = binop(SSym('inst/-'), Binop.SUB) env[SSym('inst/*')] = binop(SSym('inst/*'), Binop.MUL) env[SSym('inst//')] = binop(SSym('inst//'), Binop.DIV) env[SSym('inst/%')] = binop(SSym('inst/%'), Binop.MOD) env[SSym('inst/number=')] = binop(SSym('inst/number='), Binop.NUM_EQ) env[SSym('inst/symbol=')] = binop(SSym('inst/symbol='), Binop.SYM_EQ) env[SSym('inst/pointer=')] = binop(SSym('inst/pointer='), Binop.PTR_EQ) env[SSym('inst/number<')] = binop(SSym('inst/number<'), Binop.NUM_LT)
def to_param(self) -> bytecode.NumLit: return bytecode.NumLit(self)
def visit_SNum(self, num: sexp.SNum) -> None: self.result = bytecode.NumLit(num)