In general, it is a danger sign if a generator references a TN that isn’t an operand or temporary, since lifetime analysis hasn’t been done for that use. We are doing weird stuff for the old-cont and return-pc passing locations, hoping that the conflicts at the called function have the desired effect. Other stuff? When a function returns unknown values, we don’t reference the values locations when a single-value return is done. But nothing is live at a return point anyway.
Have a way for template conversion to special-case constant arguments? How about: If an arg restriction is (:satisfies [<predicate function>]), and the corresponding argument is constant, with the constant value satisfying the predicate, then (if any other restrictions are satisfied), the template will be emitted with the literal value passed as an info argument. If the predicate is omitted, then any constant will do.
We could sugar this up a bit by allowing (:member <object>*) for (:satisfies (lambda (x) (member x ’(<object>*))))
We could allow this to be translated into a Lisp type by adding a new Constant type specifier. This could only appear as an argument to a function type. To satisfy (Constant <type>), the argument must be a compile-time constant of the specified type. Just Constant means any constant (i.e. (Constant *)). This would be useful for the type constraints on ICR transforms.
Constant TNs: we count on being able to indirect to the leaf, and don’t try to wedge the information into the offset. We set the FSC to an appropriate immediate SC.
Allow “more operands” to VOPs in define-vop. You can’t do much with the more operands: define-vop just fills in the cost information according to the loading costs for a SC you specify. You can’t restrict more operands, and you can’t make local preferences. In the generator, the named variable is bound to the TN-ref for the first extra operand. This should be good enough to handle all the variable arg VOPs (primarily function call and return). Usually more operands are used just to get TN lifetimes to work out; the generator actually ignores them.
Variable-arg VOPs can’t be used with the VOP macro. You must use VOP*. VOP* doesn’t do anything with these extra operand except stick them on the ends of the operand lists passed into the template. VOP* is often useful within the convert functions for non-VOP templates, since it can emit a VOP using an already prepared TN-Ref list.
It is pretty basic to the whole primitive-type idea that there is only one primitive-type for a given lisp type. This is really the same as saying primitive types are disjoint. A primitive type serves two somewhat unrelated purposes: – It is an abstraction of a Lisp type used to select type specific operations. Originally kind of an efficiency hack, but it lets a template’s type signature be used both for selection and operand representation determination. – It represents a set of possible representations for a value (SCs). The primitive type is used to determine the legal SCs for a TN, and is also used to determine which type-coercion/move VOP to use.
There are basically three levels of target dependence:
– Code in the “front end” (before VMR conversion) deals only with Lisp semantics, and is totally target independent.
– Code after VMR conversion and before code generation depends on the VM, but should work with little modification across a wide range of “conventional” architectures.
– Code generation depends on the machine’s instruction set and other implementation details, so it will have to be redone for each implementation. Most of the work here is in defining the translation into assembly code of all the supported VOPs.