Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiler calculated values support #720

Closed
lee-elenbaas opened this issue Sep 22, 2014 · 17 comments
Closed

Compiler calculated values support #720

lee-elenbaas opened this issue Sep 22, 2014 · 17 comments
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@lee-elenbaas
Copy link

I would love to see support for compiler calculated constants:
Things like:

var msg = 'Hello'+' '+'world'+'!';
var ratio = 3/1;
var height = 30;
var width = height * ratio;

i like them to be translated to something like this:

var msg = 'Hello world!'; // 'Hello'+' '+'world'+'!'
var ratio = 3; // 3/1
var height = 30;
var width = 90; // height * ratio

This will allow writing a more readable code, without paying the computational price at run-time.

This idea can be expanded to also support pre-calculation of functional only code:

module x {
function fib(n) { // yes i know very naive implementation with no thinking about performance
  if (n <= 0) return 1;
  return fib(n-1)+fib(n-2);
}
export var v = fib(5);
}

can be translated to:

var x;
(function (x) {
    // omitted since it is unused outside its scope, and all its uses are pre-calculated
    // function fib(n) {
    //     if (n <= 0)
    //         return 1;
    //     return fib(n - 1) + fib(n - 2);
    // }
    x.v = 8; // fib(5)
})(x || (x = {}));

this will force the compiler to mark as part of its type information. whether something is functional or has side effects (accessing anything outside its internal scope), and where code is usable from, and whether or not it is ever used from within any function that already exists.

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds labels Sep 22, 2014
@danquirk
Copy link
Member

Are you aware of any existing languages that behave like this? The two parts of your request are fairly distinct from one another and both large, fundamental pillars of a type system. I'm not sure of any languages that do the first suggestion (maybe some techniques with C++ template metaprogramming) while the latter is a core feature in some functional languages but which have huge ramifications for the language as a whole. It's hard to imagine us doing either of these things given our current goals and constraints.

@mhegazy
Copy link
Contributor

mhegazy commented Sep 22, 2014

constant propagation or constant folding is a common compiler optimization. I would see us doing something like that if we support minification.

@RyanCavanaugh
Copy link
Member

We might do constant propagation, but I don't think there's any chance of evaluating user functions at compile-time. What if fib has a bug and hangs? Plus, the set of useful functions you could write that don't depend on possibly-mutated values in the global scope is basically empty.

@lee-elenbaas
Copy link
Author

I agree that it is two separate requests, C++ currently support both - and this gives a lot of power to C++.
In my opinion C++ and TypeScript share the compiler phase as their source of power, the fact that the code you write is not the code being run, but instead there is a compiler that enhances it and translate it gives a huge amount of power. This together with the fact that build CPU cycles are a lot cheaper then runtime cpu cycles (both in computation intensive programs, and real time where c++ dominates and in browser where TypeScript resides)
Currently i think TypeScript does not make enough use of that source of power, and i would like to see more of that being used.

The first part, pre-calculation of constants is very easy to perform, but i think it is best done in the context of the second one, and not part of minification.

Analysis of what function/variable is accessible from outside and what not is very easy to do (the compiler already check for accessibility when you try to reference a symbol) If a symbol is not accessible from outside - and not from a code that is references from outside - it should be a natural candidate to be optimized out (call it elimination of simple case of dead code) - i think everyone will appreciate a compiler warning if something like this is found (and here we have another feature request in this thread)

Finding out whether a function has side-effects is just as easy:

  1. If the symbol is accessible from the global scope - it has the potential of a side-effect since it can be proxied by other code
  2. If it uses some other symbol that is outside its own scope (instance variable, module variable, global accessible symbol... - It has a side-effect
    Anything else can be treated as functional code - and i really see no reason not to execute it by the compiler (that is written in TypeScript inside some JS VM) and use the resulting value instead of the call itself.

I agree that opening this up will offer a completely new way to program in TypeScript that is not avaliable in JS - TypeScript meta programming. But i see that as a great thing, not as a problem.

@lee-elenbaas
Copy link
Author

@RyanCavanaugh I disagree on both points:

if there is a bug in my code - i prefer my code to hang during compilation (timeout can server as a way to handle this) and not at run-time. In fact every error i can move over to the compile time is a benefit in my mind (this is the major advantage of Type safety over contract libraries)

Writing code that does not access global values but accept the necessary values as parameters is considered good coding practice and is encouraged almost everywhere - that does not mean that the values do not come from the global world, it just means that the function does not access the global world aside from the parameters it gains.
(And yes this is a complication i did not address in my previous comment - if the code calls a function of a given parameter, or changes it - then it has a side effect)
But i disagree that there are only a limited number of functions that do that, here are examples:
template generation, function wrapping, math functions, string manipulations are all functions that exists in this manner (yes there are also other variations of them), JS is talked about as enabling functional programming - and functional programming is exactly what i am referring too here.

We do have one problem more complication i did not addressed with executing code at compile time - and that is function objects and instance objects - those will have to be replaced by code that generates them by the compiler, this can lead to a slightly simplified code, but still code.

@mirhagk
Copy link

mirhagk commented Sep 23, 2014

I'd love to see a compile time option that does optimization techniques. I don't think it should be the default (I love that typescript is like 95% the same as the generated javascript), but I do think it should be available.

@RyanCavanaugh

We might do constant propagation, but I don't think there's any chance of evaluating user functions at compile-time.

I don't think there's any need to. You just need dead code elimination, constant propagation and an inliner. It wouldn't necessarily handle this specific case with the fib code, but it could optimize away a lot of similar functions. (In theory it could handle this case through repeated inlining, but it probably shouldn't handle recursive functions like this, could cause the compiler to take minutes just optimizing fib(200)).

@lee-elenbaas
Copy link
Author

@nathan

I agree that it should be controlled by compiler options, maintaning the
code as is is great for debugging.
I disagree about function inlining this is where the JS VMs are already
strong.
Calculating fib(200) will probably fail anyway due to stack overflow, but
if i make use of some large primery number that i generate using some
formula/algorithm it will be nice if this could be optimized on my build
server before deployment. And i dont really care how long that will take.
And once the call is replaced by the result, then dead code elimination can
kick in.

While we talk about compiler options, it will be nice to get the
information the compiler gather on each symbol as a comment next to its
declaration (triggered by option of course, and here is one more feture in
this thread :-) )
בתאריך 23 בספט 2014 15:46, "Nathan Jervis" [email protected] כתב:

I'd love to see a compile time option that does optimization techniques. I
don't think it should be the default (I love that typescript is like 95%
the same as the generated javascript), but I do think it should be
available.

@RyanCavanaugh https://github.com/RyanCavanaugh

We might do constant propagation, but I don't think there's any chance of
evaluating user functions at compile-time.

I don't think there's any need to. You just need dead code elimination,
constant propagation and an inliner. It wouldn't necessarily handle this
specific case with the fib code, but it could optimize away a lot of
similar functions. (In theory it could handle this case through repeated
inlining, but it probably shouldn't handle recursive functions like this,
could cause the compiler to take minutes just optimizing fib(200)).


Reply to this email directly or view it on GitHub
#720 (comment)
.

@nathan
Copy link

nathan commented Sep 23, 2014

You probably meant @mirhagk.

@lee-elenbaas
Copy link
Author

Probably, from my cellphone email i have limited view of the gh names
בתאריך 23 בספט 2014 23:06, "Nathan Dinsmore" [email protected] כתב:

You probably meant @mirhagk https://github.com/mirhagk.


Reply to this email directly or view it on GitHub
#720 (comment)
.

@CyrusNajmabadi
Copy link
Contributor

I'm not sure why this would be something you'd need the compiler to do. This sort of constant propagation is most appropriately handled by the actual runtime (which has all the real data to do this accurately).

@mirhagk
Copy link

mirhagk commented Sep 24, 2014

Yes this is true that the runtime does all of this.

Perhaps if an optimizing compiler is to be considered, a different code gen philosophy could be considered. Perhaps TypeScript could generate asm.js as a compiler target. With that compile time optimizations would likely be appreciated.

@RyanCavanaugh
Copy link
Member

See #375 re: asm.js.

@mirhagk
Copy link

mirhagk commented Sep 24, 2014

Yeah so basically no shot. Better off with compiling to something like LLVM then, which is a LOT more work than the transpiling that typescript currently does.

@lee-elenbaas
Copy link
Author

When i write typescript i write it to run on the JS runtime in the node.js or browser envirenment i intend for it to run.
but i use the compiler as a tool to make my programming life easier.

I use it to ease my code by using a better syntax (TypeScript & ES6 over ES5/3)
I use it to help me catch simple logic errors by specifying type restrictions wherever appropriate (knowing that those restrictions are compile time only)

I use linting (another compiler) to help me generate more readable code

if that compiler phase can take one more step and optimize my code it will be great.
Elimination of dead code is one way of doing it.
Calculating all values that it is possible to calculate at compiler time rather then at runtime is another great way.
Eliminating scope held instances of unreachable code is a third one.

This is not the same as compile to asm.js or LLVM since i still want it to run on the same runtime environment i intended for it to run in the first place - with the same rules about what can be done to it. With the same ability to mold the resolting JS objects using other (including non typescript) code.

I am looking over some code written by others in the last few days, it is filled with utility functions that are only accessible from a certain code segment, and are only used when the code load (static initialization) - most of this code can be optimized out. I do not want to do that by hand, since the separation of that code to its own functions makes sense to me in terms of code design and readability. but why maintain all that code in the runtime? those are functions that are memory leak in their nature. they are defined inside a specific scope, and can not be accessed from outside, they are called once and only once at the initialization of that scope, and then those function objects just sit around with their code until the runtime dismiss their scope (possibly never).
A good VM will know to page out those function objects, so they will sit on the disk rather then on memory - but who said my code run on a good VM? All i do know is that it will run on a VM, and that it passed through my compiler - so whatever work the compiler can offload from the VM, the better.

Will something like this create a new different language - it sure will, currently TypeScript is good for people that want to write JS with types (C with classes comes to mind) but the compiler offer a power that can lift TypeScript to higher places, and still maintain it JS compatible.

@lee-elenbaas
Copy link
Author

I forked TypeScript to see if i can start working on this on my own, not sure how slowly that will take me, but if anyone is willing to help, just send a relevant PR

once some milestone is reached, i will send a PR back to here

@mirhagk
Copy link

mirhagk commented Sep 29, 2014

@lee-elenbaas dead code elimination and constant propagation get you the majority of what you want here. The fib optimization is a very controversial optimization, since recursive function definitions could very easily take minutes, or hours to run, if it finishes at all.

The google closure compiler actually optimizes javascript like this (in fact doing all of the optimizations except the fib one if you have advanced optimizations on). My recommendation would be to pass it to closure as a post-build step. Any additional optimizations (like the fib one) would be useful to the closure compiler as well, so I'd suggest forking that if you'd like to add optimizations.

It's possibly worth investing time to see if there are certain optimizations that closure could make if it had access to the type information, but offhand I'm unaware of any serious optimizations it could make (there are certainly many that the runtime could make, but closure still compiles to javascript)

@lee-elenbaas
Copy link
Author

@mirhagk After diving in a bit to closure, i think your are right
thanks for the pointer

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

7 participants