llvm-project/llvm/test/CodeGen/X86/preallocated.ll

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

188 lines
6.0 KiB
LLVM
Raw Normal View History

Reland [X86] Codegen for preallocated See https://reviews.llvm.org/D74651 for the preallocated IR constructs and LangRef changes. In X86TargetLowering::LowerCall(), if a call is preallocated, record each argument's offset from the stack pointer and the total stack adjustment. Associate the call Value with an integer index. Store the info in X86MachineFunctionInfo with the integer index as the key. This adds two new target independent ISDOpcodes and two new target dependent Opcodes corresponding to @llvm.call.preallocated.{setup,arg}. The setup ISelDAG node takes in a chain and outputs a chain and a SrcValue of the preallocated call Value. It is lowered to a target dependent node with the SrcValue replaced with the integer index key by looking in X86MachineFunctionInfo. In X86TargetLowering::EmitInstrWithCustomInserter() this is lowered to an %esp adjustment, the exact amount determined by looking in X86MachineFunctionInfo with the integer index key. The arg ISelDAG node takes in a chain, a SrcValue of the preallocated call Value, and the arg index int constant. It produces a chain and the pointer fo the arg. It is lowered to a target dependent node with the SrcValue replaced with the integer index key by looking in X86MachineFunctionInfo. In X86TargetLowering::EmitInstrWithCustomInserter() this is lowered to a lea of the stack pointer plus an offset determined by looking in X86MachineFunctionInfo with the integer index key. Force any function containing a preallocated call to use the frame pointer. Does not yet handle a setup without a call, or a conditional call. Does not yet handle musttail. That requires a LangRef change first. Tried to look at all references to inalloca and see if they apply to preallocated. I've made preallocated versions of tests testing inalloca whenever possible and when they make sense (e.g. not alloca related, inalloca edge cases). Aside from the tests added here, I checked that this codegen produces correct code for something like ``` struct A { A(); A(A&&); ~A(); }; void bar() { foo(foo(foo(foo(foo(A(), 4), 5), 6), 7), 8); } ``` by replacing the inalloca version of the .ll file with the appropriate preallocated code. Running the executable produces the same results as using the current inalloca implementation. Reverted due to unexpectedly passing tests, added REQUIRES: asserts for reland. Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77689
2020-03-17 03:32:36 +08:00
; RUN: llc < %s -mtriple=i686-pc-win32 | FileCheck %s
declare token @llvm.call.preallocated.setup(i32)
declare i8* @llvm.call.preallocated.arg(token, i32)
%Foo = type { i32, i32 }
declare void @init(%Foo*)
declare void @foo_p(%Foo* preallocated(%Foo))
define void @one_preallocated() {
; CHECK-LABEL: _one_preallocated:
%t = call token @llvm.call.preallocated.setup(i32 1)
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: subl $8, %esp
; CHECK: calll _foo_p
call void @foo_p(%Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
ret void
}
define void @one_preallocated_two_blocks() {
; CHECK-LABEL: _one_preallocated_two_blocks:
%t = call token @llvm.call.preallocated.setup(i32 1)
br label %second
second:
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: subl $8, %esp
; CHECK: calll _foo_p
call void @foo_p(%Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
ret void
}
define void @preallocated_with_store() {
; CHECK-LABEL: _preallocated_with_store:
; CHECK: subl $8, %esp
%t = call token @llvm.call.preallocated.setup(i32 1)
; CHECK: leal (%esp), [[REGISTER:%[a-z]+]]
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
%p0 = getelementptr %Foo, %Foo* %b, i32 0, i32 0
%p1 = getelementptr %Foo, %Foo* %b, i32 0, i32 1
store i32 13, i32* %p0
store i32 42, i32* %p1
; CHECK-DAG: movl $13, ([[REGISTER]])
; CHECK-DAG: movl $42, 4([[REGISTER]])
; CHECK-NOT: subl {{\$[0-9]+}}, %esp
; CHECK-NOT: pushl
; CHECK: calll _foo_p
call void @foo_p(%Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
ret void
}
define void @preallocated_with_init() {
; CHECK-LABEL: _preallocated_with_init:
; CHECK: subl $8, %esp
%t = call token @llvm.call.preallocated.setup(i32 1)
; CHECK: leal (%esp), [[REGISTER:%[a-z]+]]
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: pushl [[REGISTER]]
; CHECK: calll _init
call void @init(%Foo* %b)
; CHECK-NOT: subl {{\$[0-9]+}}, %esp
; CHECK-NOT: pushl
; CHECK: calll _foo_p
call void @foo_p(%Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
ret void
}
declare void @foo_p_p(%Foo* preallocated(%Foo), %Foo* preallocated(%Foo))
define void @two_preallocated() {
; CHECK-LABEL: _two_preallocated:
%t = call token @llvm.call.preallocated.setup(i32 2)
%a1 = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b1 = bitcast i8* %a1 to %Foo*
%a2 = call i8* @llvm.call.preallocated.arg(token %t, i32 1) preallocated(%Foo)
%b2 = bitcast i8* %a2 to %Foo*
; CHECK: subl $16, %esp
; CHECK: calll _foo_p_p
call void @foo_p_p(%Foo* preallocated(%Foo) %b1, %Foo* preallocated(%Foo) %b2) ["preallocated"(token %t)]
ret void
}
declare void @foo_p_int(%Foo* preallocated(%Foo), i32)
define void @one_preallocated_one_normal() {
; CHECK-LABEL: _one_preallocated_one_normal:
; CHECK: subl $12, %esp
%t = call token @llvm.call.preallocated.setup(i32 1)
; CHECK: leal (%esp), [[REGISTER:%[a-z]+]]
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: pushl [[REGISTER]]
; CHECK: calll _init
call void @init(%Foo* %b)
; CHECK-NOT: subl {{\$[0-9]+}}, %esp
; CHECK-NOT: pushl
; CHECK: movl $2, 8(%esp)
; CHECK: calll _foo_p_int
call void @foo_p_int(%Foo* preallocated(%Foo) %b, i32 2) ["preallocated"(token %t)]
ret void
}
declare void @foo_ret_p(%Foo* sret(%Foo), %Foo* preallocated(%Foo))
Reland [X86] Codegen for preallocated See https://reviews.llvm.org/D74651 for the preallocated IR constructs and LangRef changes. In X86TargetLowering::LowerCall(), if a call is preallocated, record each argument's offset from the stack pointer and the total stack adjustment. Associate the call Value with an integer index. Store the info in X86MachineFunctionInfo with the integer index as the key. This adds two new target independent ISDOpcodes and two new target dependent Opcodes corresponding to @llvm.call.preallocated.{setup,arg}. The setup ISelDAG node takes in a chain and outputs a chain and a SrcValue of the preallocated call Value. It is lowered to a target dependent node with the SrcValue replaced with the integer index key by looking in X86MachineFunctionInfo. In X86TargetLowering::EmitInstrWithCustomInserter() this is lowered to an %esp adjustment, the exact amount determined by looking in X86MachineFunctionInfo with the integer index key. The arg ISelDAG node takes in a chain, a SrcValue of the preallocated call Value, and the arg index int constant. It produces a chain and the pointer fo the arg. It is lowered to a target dependent node with the SrcValue replaced with the integer index key by looking in X86MachineFunctionInfo. In X86TargetLowering::EmitInstrWithCustomInserter() this is lowered to a lea of the stack pointer plus an offset determined by looking in X86MachineFunctionInfo with the integer index key. Force any function containing a preallocated call to use the frame pointer. Does not yet handle a setup without a call, or a conditional call. Does not yet handle musttail. That requires a LangRef change first. Tried to look at all references to inalloca and see if they apply to preallocated. I've made preallocated versions of tests testing inalloca whenever possible and when they make sense (e.g. not alloca related, inalloca edge cases). Aside from the tests added here, I checked that this codegen produces correct code for something like ``` struct A { A(); A(A&&); ~A(); }; void bar() { foo(foo(foo(foo(foo(A(), 4), 5), 6), 7), 8); } ``` by replacing the inalloca version of the .ll file with the appropriate preallocated code. Running the executable produces the same results as using the current inalloca implementation. Reverted due to unexpectedly passing tests, added REQUIRES: asserts for reland. Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D77689
2020-03-17 03:32:36 +08:00
define void @nested_with_init() {
; CHECK-LABEL: _nested_with_init:
%tmp = alloca %Foo
%t1 = call token @llvm.call.preallocated.setup(i32 1)
; CHECK: subl $12, %esp
%a1 = call i8* @llvm.call.preallocated.arg(token %t1, i32 0) preallocated(%Foo)
%b1 = bitcast i8* %a1 to %Foo*
; CHECK: leal 4(%esp), [[REGISTER1:%[a-z]+]]
%t2 = call token @llvm.call.preallocated.setup(i32 1)
; CHECK: subl $12, %esp
%a2 = call i8* @llvm.call.preallocated.arg(token %t2, i32 0) preallocated(%Foo)
; CHECK: leal 4(%esp), [[REGISTER2:%[a-z]+]]
%b2 = bitcast i8* %a2 to %Foo*
call void @init(%Foo* %b2)
; CHECK: pushl [[REGISTER2]]
; CHECK: calll _init
call void @foo_ret_p(%Foo* %b1, %Foo* preallocated(%Foo) %b2) ["preallocated"(token %t2)]
; CHECK-NOT: subl {{\$[0-9]+}}, %esp
; CHECK-NOT: pushl
; CHECK: calll _foo_ret_p
call void @foo_ret_p(%Foo* %tmp, %Foo* preallocated(%Foo) %b1) ["preallocated"(token %t1)]
; CHECK-NOT: subl {{\$[0-9]+}}, %esp
; CHECK-NOT: pushl
; CHECK: calll _foo_ret_p
ret void
}
declare void @foo_inreg_p(i32 inreg, %Foo* preallocated(%Foo))
define void @inreg() {
; CHECK-LABEL: _inreg:
%t = call token @llvm.call.preallocated.setup(i32 1)
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: subl $8, %esp
; CHECK: movl $9, %eax
; CHECK: calll _foo_inreg_p
call void @foo_inreg_p(i32 9, %Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
ret void
}
declare x86_thiscallcc void @foo_thiscall_p(i8*, %Foo* preallocated(%Foo))
define void @thiscall() {
; CHECK-LABEL: _thiscall:
%t = call token @llvm.call.preallocated.setup(i32 1)
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: subl $8, %esp
; CHECK: xorl %ecx, %ecx
; CHECK: calll _foo_thiscall_p
call x86_thiscallcc void @foo_thiscall_p(i8* null, %Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
ret void
}
declare x86_stdcallcc void @foo_stdcall_p(%Foo* preallocated(%Foo))
declare x86_stdcallcc void @i(i32)
define void @stdcall() {
; CHECK-LABEL: _stdcall:
%t = call token @llvm.call.preallocated.setup(i32 1)
%a = call i8* @llvm.call.preallocated.arg(token %t, i32 0) preallocated(%Foo)
%b = bitcast i8* %a to %Foo*
; CHECK: subl $8, %esp
; CHECK: calll _foo_stdcall_p@8
call x86_stdcallcc void @foo_stdcall_p(%Foo* preallocated(%Foo) %b) ["preallocated"(token %t)]
; CHECK-NOT: %esp
; CHECK: pushl
; CHECK: calll _i@4
call x86_stdcallcc void @i(i32 0)
ret void
}