Hirrolot
Abstract Interpretation: From 0, 1, To ∞ [pdf]
This topic was brought up many times in the past. Most of the time it was suggested to use speculative approaches to detect blowups, but I haven't actually seen any attempts to predict them before supercompilation. For example, while I was writing Mazeppa, I've seen many times that blowups occur because some function call gives insufficient information that is subsequently pattern-matched by another function, and since it cannot be properly pattern-matched at compile-time, a lot of code duplication takes place.
I'm leaning towards a kind of "abstract interpretation pass" before supercompilation to approximate "good" and "bad" interactions between functions. After all, given that offline analyses exist even for harder problems such as detecting non-termination, why cannot we understand how supercompilation gives us desirable results?
1. The approach involves magic numbers to implement heuristics. They worked for the samples provided by the authors, but will they work for large programs?
2. I think that there's a smarter approach to detect blowups. Specifically, there can be "good" and "bad" interactions in the form `f(g(...), ...)`, where `f` and `g` are functions; an interaction is good if `g` produces information that is known at compile-time (to be subsequently deconstructed by `f`), and bad if it doesn't. If the interaction is bad, `f` and `g` should be transformed separately.
In general, I see manual annotations as a low-level mechanism that can be used for predictable supercompilation. We haven't yet implemented heuristics that would guarantee predictability, but this is on our priority list.
> Are there any other notable differences between this approach and call-by-value constructors?
Yes, lazy constructors must be implemented differently than eager ones. The standard approach is to enclose constructor arguments under an environment of values, just as we implement closure functions. I don't think that supercompilation can remove all laziness from the constructors.
[1] Jonsson, Peter & Nordlander, Johan. (2011). Taming code explosion in supercompilation. PERM'11 - Proceedings of the 20th ACM SIGPLAN Workshop on Partial Evaluation and Program Manipulation. 33-42. 10.1145/1929501.1929507.
Show HN: Rust-ontologist – A tool for visualizing Rust codebases in seconds
Comefrom
Artificial Intelligence Prepares for 2001 (1983)
Abstract Interpretation as a Programming Language
Framework for lifting x86, amd64, aarch64, sparc32, and sparc64 to LLVM bitcode
A multi-level tensor algebra superoptimizer
There's also a great talk on the matter [1], if somebody is interested in formalities.
[1] L. Augustsson. Compiling Pattern Matching. In Functional Programming Languages and Computer Architecture, pages 368– 381, 1985.
[2] P. Wadler. Efficient Compilation of Pattern Matching. In S.L. Peyton Jones, editor, The Implementation of Functional Programming Languages, pages 78–103. Prentice Hall, 1987.
Wadler's Law
In addition to that, I don't always find the constructor/method approach more readable. It _can_ be readable, if you design your API with care; however, it is common to group related data into records in the FP world as well, which mimicks OOP constructors, in a sense.
However, I agree with the wording of the original comment: when you _pass_ some arguments to a constructor and then to a method, this is (at least conceptually) partial application.
Size Optimization Tricks
The syntax of C in Backus-Naur Form
The Principle of Categorical Harmony
In fact, supercompilation subsumes many common optimizations, such as function inlining, constant propagation, list fusion, etc. So we can say that it's actually used "to a small degree", although I wouldn't really call it "supercompilation" per se.
The reason that full-blown supercompilation is not used is that it's too unpredictable -- the direct consequence of being too smart. I've elaborated on it in the final section of the blog post.