logoalt Hacker News

kragen10/12/20242 repliesview on HN

Normally I would assume you were right, but the list of things supported in the compiler includes these items:

List functions: car, cdr

Arithmetic functions: +, -, *, /, mod, 1+, 1-

Arithmetic comparisons: =, <, <=, >, >=, /=

So I think the compiler may not be able to compile calls to arbitrary functions? Maybe I should read the code.


Replies

kazinator10/12/2024

Handling specific functions is optional. If the compiler can compile a function call, it can compile a call to the + or car functions, which then have to be president in the run-time.

Functions like these are obvious targets for special recognition and inlining. Arithmetic code won't be as fast as it could be if every + has to be a call to a function, but it will work.

A compiled Lisp implementation can be bootstrapped to the point where the definition of car in the library looks like:

  (defun car (x) (car x))
And similarly for some other functions. Then there are only two places in the system that know how to actually extract the car field: the compiler source code, and the corresponding compiler executable needed for bootstrapping.

In that case if you remove the car handling from the compiler then the system's self-hosting and bootstrapping ability breaks.

show 1 reply
lispm10/12/2024

> So I think the compiler may not be able to compile calls to arbitrary functions?

A Lisp compiler will by default compile a call to ANY function as a "jump subroutine" (or as a non-returning jump in the case of a tail call) machine code call. The function then has to be present at runtime (in the runtime) and the code will call it at runtime. Lisp code by default also calls a global function through its symbol's cell -> late binding.

"supported by the compiler" here probably means that the compiler can generate inline code for these functions in various cases. Thus if the call is to the function 1+ and it knows that the argument is an integer number (and, possibly, the result also has to be an integer number), then it will not use a subroutine call via the global function, but will inline the call to an integer addition machine instruction. Obviously this then defeats late binding.

If a Lisp (for a tiny machine) only has fixnum integers as its single numeric data type, then the thing is simple -> every 1+ will get an integer argument and thus one can inline it. Inlining makes for tiny computers (which uLisp was developed for) only sense if the inlined function code isn't too long and doesn't use to much of the precious memory for the machine code. Inlining OTOH like will have a positive effect in reducing execution time and a negative effect by increasing compilation time.

In languages like Common Lisp or Scheme, which have several numeric types (fixnums, bignums, floats, ratios, complex, ...) this gets more complicated. Common Lisp compilers typically use optional type declarations and type inference to determine what inlined code to generate.

show 1 reply