| //===----------------------------------------------------------------------===// | |
| // Representing sign/zero extension of function results | |
| //===----------------------------------------------------------------------===// | |
| Mar 25, 2009 - Initial Revision | |
| Most ABIs specify that functions which return small integers do so in a | |
| specific integer GPR. This is an efficient way to go, but raises the question: | |
| if the returned value is smaller than the register, what do the high bits hold? | |
| There are three (interesting) possible answers: undefined, zero extended, or | |
| sign extended. The number of bits in question depends on the data-type that | |
| the front-end is referencing (typically i1/i8/i16/i32). | |
| Knowing the answer to this is important for two reasons: 1) we want to be able | |
| to implement the ABI correctly. If we need to sign extend the result according | |
| to the ABI, we really really do need to do this to preserve correctness. 2) | |
| this information is often useful for optimization purposes, and we want the | |
| mid-level optimizers to be able to process this (e.g. eliminate redundant | |
| extensions). | |
| For example, lets pretend that X86 requires the caller to properly extend the | |
| result of a return (I'm not sure this is the case, but the argument doesn't | |
| depend on this). Given this, we should compile this: | |
| int a(); | |
| short b() { return a(); } | |
| into: | |
| _b: | |
| subl $12, %esp | |
| call L_a$stub | |
| addl $12, %esp | |
| cwtl | |
| ret | |
| An optimization example is that we should be able to eliminate the explicit | |
| sign extension in this example: | |
| short y(); | |
| int z() { | |
| return ((int)y() << 16) >> 16; | |
| } | |
| _z: | |
| subl $12, %esp | |
| call _y | |
| ;; movswl %ax, %eax -> not needed because eax is already sext'd | |
| addl $12, %esp | |
| ret | |
| //===----------------------------------------------------------------------===// | |
| // What we have right now. | |
| //===----------------------------------------------------------------------===// | |
| Currently, these sorts of things are modelled by compiling a function to return | |
| the small type and a signext/zeroext marker is used. For example, we compile | |
| Z into: | |
| define i32 @z() nounwind { | |
| entry: | |
| %0 = tail call signext i16 (...)* @y() nounwind | |
| %1 = sext i16 %0 to i32 | |
| ret i32 %1 | |
| } | |
| and b into: | |
| define signext i16 @b() nounwind { | |
| entry: | |
| %0 = tail call i32 (...)* @a() nounwind ; <i32> [#uses=1] | |
| %retval12 = trunc i32 %0 to i16 ; <i16> [#uses=1] | |
| ret i16 %retval12 | |
| } | |
| This has some problems: 1) the actual precise semantics are really poorly | |
| defined (see PR3779). 2) some targets might want the caller to extend, some | |
| might want the callee to extend 3) the mid-level optimizer doesn't know the | |
| size of the GPR, so it doesn't know that %0 is sign extended up to 32-bits | |
| here, and even if it did, it could not eliminate the sext. 4) the code | |
| generator has historically assumed that the result is extended to i32, which is | |
| a problem on PIC16 (and is also probably wrong on alpha and other 64-bit | |
| targets). | |
| //===----------------------------------------------------------------------===// | |
| // The proposal | |
| //===----------------------------------------------------------------------===// | |
| I suggest that we have the front-end fully lower out the ABI issues here to | |
| LLVM IR. This makes it 100% explicit what is going on and means that there is | |
| no cause for confusion. For example, the cases above should compile into: | |
| define i32 @z() nounwind { | |
| entry: | |
| %0 = tail call i32 (...)* @y() nounwind | |
| %1 = trunc i32 %0 to i16 | |
| %2 = sext i16 %1 to i32 | |
| ret i32 %2 | |
| } | |
| define i32 @b() nounwind { | |
| entry: | |
| %0 = tail call i32 (...)* @a() nounwind | |
| %retval12 = trunc i32 %0 to i16 | |
| %tmp = sext i16 %retval12 to i32 | |
| ret i32 %tmp | |
| } | |
| In this model, no functions will return an i1/i8/i16 (and on a x86-64 target | |
| that extends results to i64, no i32). This solves the ambiguity issue, allows us | |
| to fully describe all possible ABIs, and now allows the optimizers to reason | |
| about and eliminate these extensions. | |
| The one thing that is missing is the ability for the front-end and optimizer to | |
| specify/infer the guarantees provided by the ABI to allow other optimizations. | |
| For example, in the y/z case, since y is known to return a sign extended value, | |
| the trunc/sext in z should be eliminable. | |
| This can be done by introducing new sext/zext attributes which mean "I know | |
| that the result of the function is sign extended at least N bits. Given this, | |
| and given that it is stuck on the y function, the mid-level optimizer could | |
| easily eliminate the extensions etc with existing functionality. | |
| The major disadvantage of doing this sort of thing is that it makes the ABI | |
| lowering stuff even more explicit in the front-end, and that we would like to | |
| eventually move to having the code generator do more of this work. However, | |
| the sad truth of the matter is that this is a) unlikely to happen anytime in | |
| the near future, and b) this is no worse than we have now with the existing | |
| attributes. | |
| C compilers fundamentally have to reason about the target in many ways. | |
| This is ugly and horrible, but a fact of life. | |