Index: lib/Target/X86/X86CallingConv.td =================================================================== --- lib/Target/X86/X86CallingConv.td +++ lib/Target/X86/X86CallingConv.td @@ -590,9 +590,11 @@ // Calling convention used on Win64 def CC_X86_Win64_C : CallingConv<[ - // FIXME: Handle byval stuff. // FIXME: Handle varargs. + // Byval aggregates are passed by pointer + CCIfByVal>, + // Promote i1/v1i1 arguments to i8. CCIfType<[i1, v1i1], CCPromoteToType>, Index: lib/Target/X86/X86ISelLowering.cpp =================================================================== --- lib/Target/X86/X86ISelLowering.cpp +++ lib/Target/X86/X86ISelLowering.cpp @@ -3138,7 +3138,7 @@ } // If value is passed via pointer - do a load. - if (VA.getLocInfo() == CCValAssign::Indirect) + if (VA.getLocInfo() == CCValAssign::Indirect && !Ins[I].Flags.isByVal()) ArgValue = DAG.getLoad(VA.getValVT(), dl, Chain, ArgValue, MachinePointerInfo()); @@ -3621,13 +3621,29 @@ Arg = DAG.getBitcast(RegVT, Arg); break; case CCValAssign::Indirect: { - // Store the argument. - SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT()); - int FI = cast(SpillSlot)->getIndex(); - Chain = DAG.getStore( - Chain, dl, Arg, SpillSlot, - MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); - Arg = SpillSlot; + if (isByVal) { + // Memcpy the argument to a temporary stack slot to prevent + // the caller from seeing any modifications the callee may make + // as guaranteed by the `byval` attribute. + int FrameIdx = MF.getFrameInfo().CreateStackObject( + Flags.getByValSize(), std::max(16, (int)Flags.getByValAlign()), + false); + SDValue StackSlot = + DAG.getFrameIndex(FrameIdx, getPointerTy(DAG.getDataLayout())); + Chain = + CreateCopyOfByValArgument(Arg, StackSlot, Chain, Flags, DAG, dl); + // From now on treat this as a regular pointer + Arg = StackSlot; + isByVal = false; + } else { + // Store the argument. + SDValue SpillSlot = DAG.CreateStackTemporary(VA.getValVT()); + int FI = cast(SpillSlot)->getIndex(); + Chain = DAG.getStore( + Chain, dl, Arg, SpillSlot, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); + Arg = SpillSlot; + } break; } } Index: test/CodeGen/X86/win64-byval.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/win64-byval.ll @@ -0,0 +1,33 @@ +; RUN: llc -mtriple x86_64-w64-mingw32 %s -o - | FileCheck %s + +declare void @foo({ float, double }* byval) +@G = external constant { float, double } + +define void @bar() +{ +; Make sure we're creating a temporary stack slot, rather than just passing +; the pointer through unmodified. +; CHECK-LABEL: @bar +; CHECK: movq G+8(%rip), %rax +; CHECK: movq %rax, 40(%rsp) +; CHECK: movq G(%rip), %rax +; CHECK: movq %rax, 32(%rsp) +; CHECK: leaq 32(%rsp), %rcx + call void @foo({ float, double }* byval @G) + ret void +} + +define void @baz({ float, double }* byval %arg) +{ +; On Win64 the byval is effectively ignored on declarations, since we do +; pass a real pointer in registers. However, by our semantics if we pass +; the pointer on to another byval function, we do need to make a copy. +; CHECK-LABEL: @baz +; CHECK: movq (%rcx), %rax +; CHECK: movq 8(%rcx), %rcx +; CHECK: movq %rcx, 40(%rsp) +; CHECK: movq %rax, 32(%rsp) +; CHECK: leaq 32(%rsp), %rcx + call void @foo({ float, double }* byval %arg) + ret void +}