}
Type *ArgMemTy = nullptr;
- if (F.getCallingConv() == CallingConv::X86_INTR) {
- // IA Interrupt passes frame (1st parameter) by value in the stack.
- if (ArgNo == 0) {
- Flags.setByVal();
- // FIXME: Dependence on pointee element type. See bug 46672.
- ArgMemTy = Arg.getType()->getPointerElementType();
- }
- }
if (Flags.isByVal() || Flags.isInAlloca() || Flags.isPreallocated() ||
Flags.isByRef()) {
if (!ArgMemTy)
StrictFPUpgradeVisitor SFPV;
SFPV.visit(F);
}
+
+ if (F.getCallingConv() == CallingConv::X86_INTR &&
+ !F.arg_empty() && !F.hasParamAttribute(0, Attribute::ByVal)) {
+ Type *ByValTy = cast<PointerType>(F.getArg(0)->getType())->getElementType();
+ Attribute NewAttr = Attribute::getWithByValType(F.getContext(), ByValTy);
+ F.addParamAttr(0, NewAttr);
+ }
}
static bool isOldLoopArgument(Metadata *MD) {
default:
case CallingConv::C:
break;
+ case CallingConv::X86_INTR: {
+ Assert(F.arg_empty() || Attrs.hasParamAttribute(0, Attribute::ByVal),
+ "Calling convention parameter requires byval", &F);
+ break;
+ }
case CallingConv::AMDGPU_KERNEL:
case CallingConv::SPIR_KERNEL:
Assert(F.getReturnType()->isVoidTy(),
--- /dev/null
+; RUN: llvm-as < %s | llvm-dis | FileCheck %s
+; RUN: verify-uselistorder %s
+
+; Make sure no arguments is accepted
+; CHECK: define x86_intrcc void @no_args() {
+define x86_intrcc void @no_args() {
+ ret void
+}
+
+; CHECK: define x86_intrcc void @byval_arg(i32* byval(i32) %0) {
+define x86_intrcc void @byval_arg(i32* byval(i32)) {
+ ret void
+}
; CHECK: declare hhvm_ccc void @f.cc82()
declare hhvm_ccc void @f.hhvm_ccc()
; CHECK: declare hhvm_ccc void @f.hhvm_ccc()
-declare cc83 void @f.cc83()
+declare cc83 void @f.cc83(i8* byval(i8))
; CHECK: declare x86_intrcc void @f.cc83()
declare x86_intrcc void @f.x86_intrcc()
; CHECK: declare x86_intrcc void @f.x86_intrcc()
; CHECK: declare hhvm_ccc void @f.cc82()
declare hhvm_ccc void @f.hhvm_ccc()
; CHECK: declare hhvm_ccc void @f.hhvm_ccc()
-declare cc83 void @f.cc83()
-; CHECK: declare x86_intrcc void @f.cc83()
-declare x86_intrcc void @f.x86_intrcc()
-; CHECK: declare x86_intrcc void @f.x86_intrcc()
+declare cc83 void @f.cc83(i8* byval(i8))
+; CHECK: declare x86_intrcc void @f.cc83(i8* byval(i8))
+declare x86_intrcc void @f.x86_intrcc(i8* byval(i8))
+; CHECK: declare x86_intrcc void @f.x86_intrcc(i8* byval(i8))
declare cc84 void @f.cc84()
; CHECK: declare avr_intrcc void @f.cc84()
declare avr_intrcc void @f.avr_intrcc()
--- /dev/null
+RUN: llvm-dis %p/Inputs/x86_intrcc_upgrade.bc -o - | FileCheck %s
+
+Make sure we upgrade x86_intrcc to a byval with explicit type
+
+CHECK: define x86_intrcc void @no_args() {
+CHECK: define x86_intrcc void @non_byval_ptr_arg0(i32* byval(i32) %0)
+CHECK: define x86_intrcc void @non_byval_ptr_struct(%struct* byval(%struct) %0)
+
+CHECK: declare x86_intrcc void @no_args_decl()
+CHECK: declare x86_intrcc void @non_byval_ptr_arg0_decl(i32* byval(i32))
+CHECK: declare x86_intrcc void @non_byval_ptr_struct_decl(%struct* byval(%struct))
; Spills eax, putting original esp at +4.
; No stack adjustment if declared with no error code
-define x86_intrcc void @test_isr_no_ecode(%struct.interrupt_frame* %frame) {
+define x86_intrcc void @test_isr_no_ecode(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame) {
; CHECK-LABEL: test_isr_no_ecode:
; CHECK: pushl %eax
; CHECK: movl 12(%esp), %eax
; Spills eax and ecx, putting original esp at +8. Stack is adjusted up another 4 bytes
; before return, popping the error code.
-define x86_intrcc void @test_isr_ecode(%struct.interrupt_frame* %frame, i32 %ecode) {
+define x86_intrcc void @test_isr_ecode(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i32 %ecode) {
; CHECK-LABEL: test_isr_ecode
; CHECK: pushl %ecx
; CHECK: pushl %eax
}
; All clobbered registers must be saved
-define x86_intrcc void @test_isr_clobbers(%struct.interrupt_frame* %frame, i32 %ecode) {
+define x86_intrcc void @test_isr_clobbers(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i32 %ecode) {
call void asm sideeffect "", "~{eax},~{ebx},~{ebp}"()
; CHECK-LABEL: test_isr_clobbers
; CHECK: pushl %ebp
@f80 = common global x86_fp80 0xK00000000000000000000, align 4
; Test that the presence of x87 does not crash the FP stackifier
-define x86_intrcc void @test_isr_x87(%struct.interrupt_frame* %frame) {
+define x86_intrcc void @test_isr_x87(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame) {
; CHECK-LABEL: test_isr_x87
; CHECK-DAG: fldt f80
; CHECK-DAG: fld1
; Use a frame pointer to check the offsets. No return address, arguments start
; at EBP+4.
-define dso_local x86_intrcc void @test_fp_1(%struct.interrupt_frame* %p) #0 {
+define dso_local x86_intrcc void @test_fp_1(%struct.interrupt_frame* byval(%struct.interrupt_frame) %p) #0 {
; CHECK-LABEL: test_fp_1:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pushl %ebp
}
; The error code is between EBP and the interrupt_frame.
-define dso_local x86_intrcc void @test_fp_2(%struct.interrupt_frame* %p, i32 %err) #0 {
+define dso_local x86_intrcc void @test_fp_2(%struct.interrupt_frame* byval(%struct.interrupt_frame) %p, i32 %err) #0 {
; CHECK-LABEL: test_fp_2:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pushl %ebp
}
; Test argument copy elision when copied to a local alloca.
-define x86_intrcc void @test_copy_elide(%struct.interrupt_frame* %frame, i32 %err) #0 {
+define x86_intrcc void @test_copy_elide(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i32 %err) #0 {
; CHECK-LABEL: test_copy_elide:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pushl %ebp
@llvm.used = appending global [1 x i8*] [i8* bitcast (void (%struct.interrupt_frame*, i64)* @test_isr_sse_clobbers to i8*)], section "llvm.metadata"
; Clobbered SSE must not be saved when the target doesn't support SSE
-define x86_intrcc void @test_isr_sse_clobbers(%struct.interrupt_frame* %frame, i64 %ecode) {
+define x86_intrcc void @test_isr_sse_clobbers(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i64 %ecode) {
; CHECK-LABEL: test_isr_sse_clobbers:
; CHECK: # %bb.0:
; CHECK-NEXT: pushq %rax
; Spills rax, putting original esp at +8.
; No stack adjustment if declared with no error code
-define x86_intrcc void @test_isr_no_ecode(%struct.interrupt_frame* %frame) {
+define x86_intrcc void @test_isr_no_ecode(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame) {
; CHECK-LABEL: test_isr_no_ecode:
; CHECK: pushq %rax
; CHECK: movq 24(%rsp), %rax
; Spills rax and rcx, putting original rsp at +16. Stack is adjusted up another 8 bytes
; before return, popping the error code.
-define x86_intrcc void @test_isr_ecode(%struct.interrupt_frame* %frame, i64 %ecode) {
+define x86_intrcc void @test_isr_ecode(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i64 %ecode) {
; CHECK-LABEL: test_isr_ecode
; CHECK: pushq %rax
; CHECK: pushq %rax
}
; All clobbered registers must be saved
-define x86_intrcc void @test_isr_clobbers(%struct.interrupt_frame* %frame, i64 %ecode) {
+define x86_intrcc void @test_isr_clobbers(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i64 %ecode) {
call void asm sideeffect "", "~{rax},~{rbx},~{rbp},~{r11},~{xmm0}"()
; CHECK-LABEL: test_isr_clobbers
@f80 = common global x86_fp80 0xK00000000000000000000, align 4
; Test that the presence of x87 does not crash the FP stackifier
-define x86_intrcc void @test_isr_x87(%struct.interrupt_frame* %frame) {
+define x86_intrcc void @test_isr_x87(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame) {
; CHECK-LABEL: test_isr_x87
; CHECK-DAG: fldt f80
; CHECK-DAG: fld1
; Use a frame pointer to check the offsets. No return address, arguments start
; at RBP+4.
-define dso_local x86_intrcc void @test_fp_1(%struct.interrupt_frame* %p) #0 {
+define dso_local x86_intrcc void @test_fp_1(%struct.interrupt_frame* byval(%struct.interrupt_frame) %p) #0 {
; CHECK-LABEL: test_fp_1:
; CHECK: # %bb.0: # %entry
; CHECK-NEXT: pushq %rbp
}
; The error code is between RBP and the interrupt_frame.
-define dso_local x86_intrcc void @test_fp_2(%struct.interrupt_frame* %p, i64 %err) #0 {
+define dso_local x86_intrcc void @test_fp_2(%struct.interrupt_frame* byval(%struct.interrupt_frame) %p, i64 %err) #0 {
; CHECK-LABEL: test_fp_2:
; CHECK: # %bb.0: # %entry
; This RAX push is just to align the stack.
}
; Test argument copy elision when copied to a local alloca.
-define x86_intrcc void @test_copy_elide(%struct.interrupt_frame* %frame, i64 %err) #0 {
+define x86_intrcc void @test_copy_elide(%struct.interrupt_frame* byval(%struct.interrupt_frame) %frame, i64 %err) #0 {
; CHECK-LABEL: test_copy_elide:
; CHECK: # %bb.0: # %entry
; This RAX push is just to align the stack.
; Make sure we spill the high numbered zmm registers and K registers with the right encoding.
-define x86_intrcc void @foo(i8* %frame) {
+define x86_intrcc void @foo(i8* byval(i8) %frame) {
; CHECK64-KNL-LABEL: foo:
; CHECK64-KNL: ## %bb.0:
; CHECK64-KNL-NEXT: pushq %rax ## encoding: [0x50]
; CHECK: cld
; CHECK: call
-define x86_intrcc void @foo(i8* %frame) {
+define x86_intrcc void @foo(i8* byval(i8) %frame) {
call void @bar()
ret void
}
; CHECK-NOT: vzeroupper
; CHECK: iret
-define x86_intrcc void @foo(i8* %frame) {
+define x86_intrcc void @foo(i8* byval(i8) %frame) {
call void @bar()
ret void
}
;; In functions with 'no_caller_saved_registers' attribute, all registers should
;; be preserved except for registers used for passing/returning arguments.
;; The test checks that function "bar" preserves xmm0 register.
-;; It also checks that caller function "foo" does not store registers for callee
+;; It also checks that caller function "foo" does not store registers for callee
;; "bar". For example, there is no store/load/access to xmm registers.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ret i32 1
}
-define x86_intrcc void @foo(i8* nocapture readnone %c) {
+define x86_intrcc void @foo(i8* byval(i8) nocapture readnone %c) {
; CHECK-LABEL: foo
; CHECK-NOT: xmm
entry:
--- /dev/null
+; RUN: not llvm-as < %s 2>&1 | FileCheck %s
+
+; CHECK: Calling convention parameter requires byval
+; CHECK-NEXT: void (i32)* @non_ptr_arg0
+define x86_intrcc void @non_ptr_arg0(i32) {
+ ret void
+}
+
+; CHECK: Calling convention parameter requires byval
+; CHECK-NEXT: void (i32*)* @non_byval_ptr_arg0
+define x86_intrcc void @non_byval_ptr_arg0(i32*) {
+ ret void
+}
+
+; CHECK: Calling convention parameter requires byval
+; CHECK-NEXT: void (i32)* @non_ptr_arg0_decl
+declare x86_intrcc void @non_ptr_arg0_decl(i32)
+
+; CHECK: Calling convention parameter requires byval
+; CHECK-NEXT: void (i32*)* @non_byval_ptr_arg0_decl
+declare x86_intrcc void @non_byval_ptr_arg0_decl(i32*)