I've given this more thought and I think that we can possibly get rid of the HIR instructions completely. At the moment, the AST->IR translation inserts "HIR" instructions, such as add, and these will get replaced by calls to the corresponding primitive later, in a subsequent pass which also does inlining. However, what I could do is issue the calls directly. This would completely eliminate many of the instruction classes.
When an add between boxed values is encountered in the AST translation, this would issue a call to the add primitive function (which could later be inlined). Adds between typed values (such as adds between i64s) would instead be translated directly to the add instruction. Then, in the primitive code, if one wishes to use the machine add to add two boxed values without shifting, one could do iir.add(v1, v2). This instruction would never be replaced, and so the back-end would receive it directly.
Following Marc's suggestion, we could add a second type parameter to the load and store instructions to allow them to perform integer casts. In this way, it becomes possible to load an i32 value directly into an i64 value (load_i32_i64), or to store an i64 value into an i32 in memory. This also would minimize the need for casts in the implementation of primitives.
Note that the icast instruction would still be useful, however, if we ever wish to unbox an integer value, or convert a raw pointer into a boxed value (it's a convenient way of indicating the change in type at the IR level).
- Maxime
On 10-09-22 05:41 PM, Marc Feeley wrote:
On 2010-09-22, at 4:24 PM, chevalma@iro.umontreal.ca wrote:
I made some small changes to the IR and renamed the gte, lte and neq instructions as suggested today. If you do a checkout of my latest commit, doing a make run will show the updated mnemonics.
However, if I may, I would have a suggestion for the code generation. Instead of using mnemonics, we should probably "install" code generation functions on the instruction classes. For example:
AddInstr.prototype.genCode = function (...) { ... }
This will avoid relying on mnemonic names, and having redundant code for many variants of the same instruction. The code generation code can then find out the type of the operands by using this.uses[i].type, which refers to an IRType object. It can also find its own expected output type by using this.type.
Also, following todays discussion, I'm pondering whether or not I should have completely distinct HIR/MIR instructions. Right now, the add on boxed values, for example, has the semantic of a call to the add() primitive function (a JavaScript add), whereas the same add instruction on i64 values represents the machine word add instruction. The icast instructions are then needed if we want to directly perform a machine add on boxed values without doing any shifting, to indicate that we want to perform an integer add.
It would be possible to, instead, have HIRAdd and MIRAdd instructions. The HIRAdd instructions would then be translated either into a call or into an MIRAdd which would represent the machine instruction add, depending on the types of the input arguments supplied. This would make it possible to eliminate the cast instructions in this case, and directly perform an MIRAdd on boxed values. It would have the disadvantage that arithmetic, bitwise and comparison instruction classes would be duplicated for the two possible semantics, however.
This sounds like an interesting idea. It is in line with my position about having more abstract operations (which for example abstract the data representation). I'm not sure you have to call them HIRAdd and MIRAdd... it might be appropriate to mix both types of adds at the same IR level. I prefer to have families of related "add" operation that operate on different data representations. The families would differ on the *semantics* of the operation (i.e. what is the relation between the input and output). So
Add 2 JavaScript numbers to yield a JavaScript number:
add_js: (boxed,boxed) -> boxed
Add 2 integers with wraparound (operations that are useful if no overflow is possible, by design or program analysis):
iadd_wrap: (fixnum,fixnum) -> fixnum iadd_wrap: (int32,int32) -> int32 iadd_wrap: (int64,int64) -> int64
Add 2 integers with overflow detection (operations useful to implement add_js):
iadd_ovf: (fixnum,fixnum) -> fixnum normal: lbl1 overflow: lbl2 iadd_ovf: (int32,int32) -> int32 normal: lbl1 overflow: lbl2 iadd_ovf: (int64,int64) -> int64 normal: lbl1 overflow: lbl2
Add 2 floating point numbers (operations useful to implement add_js):
fadd: (flonum,flonum) -> flonum fadd: (float64,float64) -> float64
Marc
Tachyon-list mailing list Tachyon-list@iro.umontreal.ca https://webmail.iro.umontreal.ca/mailman/listinfo/tachyon-list