| Ok, here are my comments and suggestions about the LLVM instruction set. | |
| We should discuss some now, but can discuss many of them later, when we | |
| revisit synchronization, type inference, and other issues. | |
| (We have discussed some of the comments already.) | |
| o We should consider eliminating the type annotation in cases where it is | |
| essentially obvious from the instruction type, e.g., in br, it is obvious | |
| that the first arg. should be a bool and the other args should be labels: | |
| br bool <cond>, label <iftrue>, label <iffalse> | |
| I think your point was that making all types explicit improves clarity | |
| and readability. I agree to some extent, but it also comes at the cost | |
| of verbosity. And when the types are obvious from people's experience | |
| (e.g., in the br instruction), it doesn't seem to help as much. | |
| o On reflection, I really like your idea of having the two different switch | |
| types (even though they encode implementation techniques rather than | |
| semantics). It should simplify building the CFG and my guess is it could | |
| enable some significant optimizations, though we should think about which. | |
| o In the lookup-indirect form of the switch, is there a reason not to make | |
| the val-type uint? Most HLL switch statements (including Java and C++) | |
| require that anyway. And it would also make the val-type uniform | |
| in the two forms of the switch. | |
| I did see the switch-on-bool examples and, while cute, we can just use | |
| the branch instructions in that particular case. | |
| o I agree with your comment that we don't need 'neg'. | |
| o There's a trade-off with the cast instruction: | |
| + it avoids having to define all the upcasts and downcasts that are | |
| valid for the operands of each instruction (you probably have thought | |
| of other benefits also) | |
| - it could make the bytecode significantly larger because there could | |
| be a lot of cast operations | |
| o Making the second arg. to 'shl' a ubyte seems good enough to me. | |
| 255 positions seems adequate for several generations of machines | |
| and is more compact than uint. | |
| o I still have some major concerns about including malloc and free in the | |
| language (either as builtin functions or instructions). LLVM must be | |
| able to represent code from many different languages. Languages such as | |
| C, C++ Java and Fortran 90 would not be able to use our malloc anyway | |
| because each of them will want to provide a library implementation of it. | |
| This gets even worse when code from different languages is linked | |
| into a single executable (which is fairly common in large apps). | |
| Having a single malloc would just not suffice, and instead would simply | |
| complicate the picture further because it adds an extra variant in | |
| addition to the one each language provides. | |
| Instead, providing a default library version of malloc and free | |
| (and perhaps a malloc_gc with garbage collection instead of free) | |
| would make a good implementation available to anyone who wants it. | |
| I don't recall all your arguments in favor so let's discuss this again, | |
| and soon. | |
| o 'alloca' on the other hand sounds like a good idea, and the | |
| implementation seems fairly language-independent so it doesn't have the | |
| problems with malloc listed above. | |
| o About indirect call: | |
| Your option #2 sounded good to me. I'm not sure I understand your | |
| concern about an explicit 'icall' instruction? | |
| o A pair of important synchronization instr'ns to think about: | |
| load-linked | |
| store-conditional | |
| o Other classes of instructions that are valuable for pipeline performance: | |
| conditional-move | |
| predicated instructions | |
| o I believe tail calls are relatively easy to identify; do you know why | |
| .NET has a tailcall instruction? | |
| o I agree that we need a static data space. Otherwise, emulating global | |
| data gets unnecessarily complex. | |
| o About explicit parallelism: | |
| We once talked about adding a symbolic thread-id field to each | |
| instruction. (It could be optional so single-threaded codes are | |
| not penalized.) This could map well to multi-threaded architectures | |
| while providing easy ILP for single-threaded onces. But it is probably | |
| too radical an idea to include in a base version of LLVM. Instead, it | |
| could a great topic for a separate study. | |
| What is the semantics of the IA64 stop bit? | |
| o And finally, another thought about the syntax for arrays :-) | |
| Although this syntax: | |
| array <dimension-list> of <type> | |
| is verbose, it will be used only in the human-readable assembly code so | |
| size should not matter. I think we should consider it because I find it | |
| to be the clearest syntax. It could even make arrays of function | |
| pointers somewhat readable. | |