================

----------------
zibi2 wrote:

> > You are right that my framing was imprecise. The real distinction is 
> > between a partial register write and a full register write.
> > CCAssignToRegAndStack<[R1L,...]> assigns the argument to the 32-bit 
> > sub-register R1L, so the caller only writes the low 32 bits of the GPR. The 
> > upper 32 bits are never touched by the call sequence.
> > CCPromoteToType with ANY_EXTEND assigns the argument to the full 64-bit 
> > register R1D. On SystemZ, ANY_EXTEND i32→i64 lowers to a zero-filling 
> > instruction (LLILL/LLILF), so the caller writes all 64 bits.
> 
> But `ANY_EXTEND` is not required or guaranteed to do that - this could change 
> at any time!
> 
> > xlc requires the caller to have written the full 64-bit register. R1L 
> > violates that because the upper half is never written. ANY_EXTEND satisfies 
> > it because the full register is written.
> 
> It doesn't make sense to say it is a requirement that the upper half is 
> "written" - what does that even mean? _What value_ is supposed to be written?
> 
The requirement is that the upper 32 bits are zero. The XPLINK64 ABI spec (z/OS 
Language Environment Vendor Interfaces) states that integer arguments smaller 
than 64 bits are passed zero-extended into the full 64-bit GPR. So a caller 
passing int -1 must put 0x00000000ffffffff in the register, not 
0xffffffffffffffff. 

> Can you be specific in what callees compiled with these compiler versions 
> actually expect to see in the upper half of the register? Is it always zero? 
> Is it zero- or sign-extended depending on the type? Anything else?
> 
Based on disassembling xlc output: xlc callees sign-extend the incoming int 
parameter themselves, unconditionally, in the callee prologue — LGFR R2,R2 — 
before the first C statement executes. They do not rely on the caller having 
done anything specific to the upper half.

So the "what does the upper half need to contain" answer is: anything — the 
callee fixes it regardless. The upper half content from the caller is 
irrelevant to xlc callees.

> It appears to me that we have two conflicting requirements here. On the one 
> hand, you say that in a callee we cannot rely on an i32 argument to always be 
> extended to i64 because some old (caller) code doesn't do that. On the other 
> hand, you say as a caller we must extend i32 arguments to i64 because some 
> old (callee) code expects that. What if that old caller code calls that old 
> callee code? That would already not work correctly, right?

They're not contradictory, they address different failure modes:
Scenario | Problem | Our fix
-- | -- | --
clang-as-callee, xlc-as-caller | xlc caller writes R1L only. If clang-callee 
has signext/zeroext on the parameter, clang emits a prologue LGFR/LLGFR that 
re-extends whatever the caller left — which may be sign-extended garbage from 
xlc. getDirect (no extend attribute) avoids this prologue. | 
classifyArgumentType returns getDirect
clang-as-caller, xlc-as-callee | At -O0, clang spills the i32 arg to a 32-bit 
stack slot and reloads it. ANY_EXTEND of a 32-bit memory load on big-endian 
S390 lowers to LGF (sign-extend load). For -1, this produces 0xffffffffffffffff 
in the GPR. The xlc callee's prologue LGFR R2,R2 then re-sign-extends 
0xffffffffffffffff → still 0xffffffffffffffff, which is wrong for what should 
be 0x00000000ffffffff (zero-extended -1). | convertValVTToLocVT uses 
ZERO_EXTEND instead of ANY_EXTEND for call args

Agreed, and that's exactly why the latest commit replaces the ANY_EXTEND in 
convertValVTToLocVT with an explicit ZERO_EXTEND when IsCallArg=true. The 
zero-extension is now an invariant of our call-lowering, not a side-effect of 
how ANY_EXTEND happens to lower today.

https://github.com/llvm/llvm-project/pull/206833
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to