1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
; RUN: opt < %s -expand-varargs -S | FileCheck %s
%va_list = type i8*
declare void @llvm.va_start(i8*)
declare void @llvm.va_end(i8*)
declare void @llvm.va_copy(i8*, i8*)
declare i32 @outside_func(i32 %arg, %va_list* %args)
define i32 @varargs_func(i32 %arg, ...) {
%arglist_alloc = alloca %va_list
%arglist = bitcast %va_list* %arglist_alloc to i8*
call void @llvm.va_start(i8* %arglist)
%result = call i32 @outside_func(i32 %arg, %va_list* %arglist_alloc)
call void @llvm.va_end(i8* %arglist)
ret i32 %result
}
; CHECK: define i32 @varargs_func(i32 %arg, i8* noalias %varargs) {
; CHECK-NEXT: %arglist_alloc = alloca i8*
; CHECK-NEXT: %arglist = bitcast i8** %arglist_alloc to i8*
; CHECK-NEXT: %arglist1 = bitcast i8* %arglist to i8**
; CHECK-NEXT: store i8* %varargs, i8** %arglist1
; CHECK-NEXT: %result = call i32 @outside_func(i32 %arg, i8** %arglist_alloc)
; CHECK-NEXT: ret i32 %result
define i32 @varargs_call1() {
%result = call i32 (i32, ...)* @varargs_func(i32 111, i64 222, i32 333)
ret i32 %result
}
; CHECK: define i32 @varargs_call1() {
; CHECK-NEXT: %vararg_buffer = alloca <{ i64, i32 }>
; CHECK-NEXT: %vararg_lifetime_bitcast = bitcast <{ i64, i32 }>* %vararg_buffer to i8*
; CHECK-NEXT: call void @llvm.lifetime.start(i64 12, i8* %vararg_lifetime_bitcast)
; CHECK-NEXT: %vararg_ptr = getelementptr <{ i64, i32 }>* %vararg_buffer, i32 0, i32 0
; CHECK-NEXT: store i64 222, i64* %vararg_ptr
; CHECK-NEXT: %vararg_ptr1 = getelementptr <{ i64, i32 }>* %vararg_buffer, i32 0, i32 1
; CHECK-NEXT: store i32 333, i32* %vararg_ptr1
; CHECK-NEXT: %vararg_func = bitcast i32 (i32, ...)* bitcast (i32 (i32, i8*)* @varargs_func to i32 (i32, ...)*) to i32 (i32, <{ i64, i32 }>*)*
; CHECK-NEXT: %result = call i32 %vararg_func(i32 111, <{ i64, i32 }>* %vararg_buffer)
; CHECK-NEXT: call void @llvm.lifetime.end(i64 12, i8* %vararg_lifetime_bitcast)
; CHECK-NEXT: ret i32 %result
; Check that the pass works when there are no variable arguments.
define i32 @call_with_zero_varargs() {
%result = call i32 (i32, ...)* @varargs_func(i32 111)
ret i32 %result
}
; CHECK: define i32 @call_with_zero_varargs() {
; We have a dummy i32 field to deal with buggy programs:
; CHECK-NEXT: %vararg_buffer = alloca <{ i32 }>
; CHECK: %vararg_func = bitcast i32 (i32, ...)* bitcast (i32 (i32, i8*)* @varargs_func to i32 (i32, ...)*) to i32 (i32, <{ i32 }>*)*
; CHECK-NEXT: %result = call i32 %vararg_func(i32 111, <{ i32 }>* %vararg_buffer)
; Check that "invoke" instructions are expanded out too.
define i32 @varargs_invoke() {
%result = invoke i32 (i32, ...)* @varargs_func(i32 111, i64 222)
to label %cont unwind label %lpad
cont:
ret i32 %result
lpad:
%lp = landingpad { i8*, i32 } personality i8* null cleanup
ret i32 0
}
; CHECK: @varargs_invoke
; CHECK: %result = invoke i32 %vararg_func(i32 111, <{ i64 }>* %vararg_buffer)
; CHECK-NEXT: to label %cont unwind label %lpad
; CHECK: cont:
; CHECK-NEXT: call void @llvm.lifetime.end(i64 8, i8* %vararg_lifetime_bitcast)
; CHECK: lpad:
; CHECK: call void @llvm.lifetime.end(i64 8, i8* %vararg_lifetime_bitcast)
define void @varargs_multiple_calls() {
%call1 = call i32 (i32, ...)* @varargs_func(i32 11, i64 22, i32 33)
%call2 = call i32 (i32, ...)* @varargs_func(i32 44, i64 55, i32 66)
ret void
}
; CHECK: @varargs_multiple_calls()
; The added allocas should appear at the start of the function.
; CHECK: %vararg_buffer{{.*}} = alloca <{ i64, i32 }>
; CHECK: %vararg_buffer{{.*}} = alloca <{ i64, i32 }>
; CHECK: %call1 = call i32 %vararg_func{{.*}}(i32 11, <{ i64, i32 }>* %vararg_buffer{{.*}})
; CHECK: %call2 = call i32 %vararg_func{{.*}}(i32 44, <{ i64, i32 }>* %vararg_buffer{{.*}})
define i32 @va_arg_i32(i8* %arglist) {
%result = va_arg i8* %arglist, i32
ret i32 %result
}
; CHECK: define i32 @va_arg_i32(i8* %arglist) {
; CHECK-NEXT: %arglist1 = bitcast i8* %arglist to i32**
; CHECK-NEXT: %arglist_current = load i32** %arglist1
; CHECK-NEXT: %result = load i32* %arglist_current
; CHECK-NEXT: %arglist_next = getelementptr i32* %arglist_current, i32 1
; CHECK-NEXT: store i32* %arglist_next, i32** %arglist1
; CHECK-NEXT: ret i32 %result
define i64 @va_arg_i64(i8* %arglist) {
%result = va_arg i8* %arglist, i64
ret i64 %result
}
; CHECK: define i64 @va_arg_i64(i8* %arglist) {
; CHECK-NEXT: %arglist1 = bitcast i8* %arglist to i64**
; CHECK-NEXT: %arglist_current = load i64** %arglist1
; CHECK-NEXT: %result = load i64* %arglist_current
; CHECK-NEXT: %arglist_next = getelementptr i64* %arglist_current, i32 1
; CHECK-NEXT: store i64* %arglist_next, i64** %arglist1
; CHECK-NEXT: ret i64 %result
define void @do_va_copy(i8* %dest, i8* %src) {
call void @llvm.va_copy(i8* %dest, i8* %src)
ret void
}
; CHECK: define void @do_va_copy(i8* %dest, i8* %src) {
; CHECK-NEXT: %vacopy_src = bitcast i8* %src to i8**
; CHECK-NEXT: %vacopy_dest = bitcast i8* %dest to i8**
; CHECK-NEXT: %vacopy_currentptr = load i8** %vacopy_src
; CHECK-NEXT: store i8* %vacopy_currentptr, i8** %vacopy_dest
; CHECK-NEXT: ret void
|