================ ---------------- 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
