What prevents the usage of a function argument as hidden pointer?

前端 未结 3 999
孤城傲影
孤城傲影 2020-12-07 00:58

I try to understand the implication of System V AMD64 - ABI\'s calling convention and looking at the following example:

struct Vec3{
    double x, y, z;
};

         


        
3条回答
  •  臣服心动
    2020-12-07 01:34

    Substantially rewritten:

    I understand, that an alias of pointer out (e.g. as global variable) could be used in do_something and thus [out] cannot be passed as hidden pointer to do_something: if it would, out would be changed inside of do_something and not when do_something returns, thus some calculations might become faulty.

    Except with respect to aliasing considerations inside do_something(), the difference in timing with respect to when *out is modified is irrelevant in the sense that use()'s caller cannot tell the difference. Such issues arise only with respect to accesses from other threads, and if that's a possibility then they arise anyway unless appropriate synchronization is applied.

    No, the issue is primarily that the ABI defines how passing arguments to functions and receiving their return values works. It specifies that

    If the type has class MEMORY, then the caller provides space for the return value and passes the address of this storage in %rdi

    (emphasis added).

    I grant that there's room for interpretation, but I take that as a stronger statement than just that the caller specifies where to store the return value. That it "provides" space means to me that the space in question belongs to the caller (which your *out does not). By analogy with argument passing, there's good reason to interpret that more specifically as saying that the caller provides space on the stack (and therefore in its own stack frame) for the return value, which in fact is exactly what you observe, though that detail doesn't really matter.

    With that interpretation, the called function is free to assume that the return-value space is disjoint from any space it can access via any pointer other than one of its arguments. That this is supplemented by a more general requirement that the return space not be aliased (i.e. not through the function arguments either) does not contradict that interpretation. It may therefore perform operations that would be incorrect if in fact the space were aliased to something else accessible to the function.

    The compiler is not at liberty to depart from the ABI specifications if the function call is to work correctly with a separately-compiled do_something() function. In particular, with separate compilation, the compiler cannot make decisions based on characteristics of the function's caller, such as aliasing information known there. If do_something() and use() were in the same translation unit, then the compiler might choose to inline so_something() into use(), or it might choose to perform the optimization you're looking for without inlining, but it cannot safely do so in the general case.

    It was suggested to me, that using restrict should solve the problem,

    restrict gives the compiler greater leeway to optimize, but that in itself does not give you any reason to expect specific optimizations that might then be possible. In fact, the language standard explicitly specifies that

    A translator is free to ignore any or all aliasing implications of uses of restrict.

    (C2011, 6.7.3.1/6)

    restrict-qualifying out expresses that the compiler doesn't need to worry about it being aliased to any other pointer accessed within the scope of a call to use(), including during the execution of functions other functions it calls. In principle, then, I could see a compiler taking advantage of that to shortcut the ABI by offering somebody else's space for the return value instead of providing space itself, but just because it could do does not mean that it will do.

    What prevents the usage of out as hidden pointer?

    ABI compliance. The caller is expected to provide space that belongs to it, not to someone else, for storage of the return value. As a practical matter, however, I don't see anything in the restrict-qualified case that would invalidate shortcutting the ABI, so I take it that that's just not an optimization that has been implemented by the compiler in question.

    NB: The desired (or very similar) behavior would be achieved for a slightly different function-signature: [...]

    That case looks like a tail-call optimization to me. I don't see anything inherently inconsistent in the compiler performing that optimization, but not the one you're asking about, even though it is, to be sure, a different example of shortcutting the ABI.

提交回复
热议问题