Skip to content

Commit dde7f4d

Browse files
authored
[NFC][clang] Add ubsan-trap-merge.ll test to show absence of nomerge considered harmful (#117657)
These testcases demonstrate that ubsan intrinsics are merged in the backend iff nomerge is missing from ubsantrap intrinsics. This is based on the observation and testcase by Vitaly Buka in #83470.
1 parent 1b68b33 commit dde7f4d

File tree

2 files changed

+367
-0
lines changed

2 files changed

+367
-0
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
2+
; RUN: llc -O3 -mtriple x86_64 -filetype asm -o - %s | FileCheck %s
3+
;
4+
; This test shows that ubsantrap can, in the absence of nomerge, be merged by
5+
; the backend into a single ud1 instruction (thus making debugging difficult).
6+
;
7+
; The LLVM IR was generated from clang/test/CodeGen/ubsan-trap-merge.c.
8+
;
9+
; ModuleID = '../clang/test/CodeGen/ubsan-trap-merge.c'
10+
source_filename = "../clang/test/CodeGen/ubsan-trap-merge.c"
11+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
12+
target triple = "x86_64-unknown-linux-gnu"
13+
14+
; Function Attrs: nounwind uwtable
15+
define dso_local range(i32 -2147483523, -2147483648) i32 @f(i32 noundef %x) local_unnamed_addr #0 {
16+
; CHECK-LABEL: f:
17+
; CHECK: # %bb.0: # %entry
18+
; CHECK-NEXT: addl $125, %edi
19+
; CHECK-NEXT: jo .LBB0_1
20+
; CHECK-NEXT: # %bb.2: # %cont
21+
; CHECK-NEXT: movl %edi, %eax
22+
; CHECK-NEXT: retq
23+
; CHECK-NEXT: .LBB0_1: # %trap
24+
; CHECK-NEXT: ud1l 2(%eax), %eax
25+
entry:
26+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 125), !nosanitize !5
27+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
28+
br i1 %1, label %trap, label %cont, !nosanitize !5
29+
30+
trap: ; preds = %entry
31+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
32+
unreachable, !nosanitize !5
33+
34+
cont: ; preds = %entry
35+
%2 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
36+
ret i32 %2
37+
}
38+
39+
; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
40+
declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #1
41+
42+
; Function Attrs: cold noreturn nounwind
43+
declare void @llvm.ubsantrap(i8 immarg) #2
44+
45+
; Function Attrs: nounwind uwtable
46+
define dso_local range(i32 -2147483521, -2147483648) i32 @g(i32 noundef %x) local_unnamed_addr #0 {
47+
; CHECK-LABEL: g:
48+
; CHECK: # %bb.0: # %entry
49+
; CHECK-NEXT: addl $127, %edi
50+
; CHECK-NEXT: jo .LBB1_1
51+
; CHECK-NEXT: # %bb.2: # %cont
52+
; CHECK-NEXT: movl %edi, %eax
53+
; CHECK-NEXT: retq
54+
; CHECK-NEXT: .LBB1_1: # %trap
55+
; CHECK-NEXT: ud1l 2(%eax), %eax
56+
entry:
57+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 127), !nosanitize !5
58+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
59+
br i1 %1, label %trap, label %cont, !nosanitize !5
60+
61+
trap: ; preds = %entry
62+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
63+
unreachable, !nosanitize !5
64+
65+
cont: ; preds = %entry
66+
%2 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
67+
ret i32 %2
68+
}
69+
70+
; Function Attrs: nounwind uwtable
71+
define dso_local range(i32 -2147483521, -2147483648) i32 @h(i32 noundef %x, i32 noundef %y) local_unnamed_addr #0 {
72+
; CHECK-LABEL: h:
73+
; CHECK: # %bb.0: # %entry
74+
; CHECK-NEXT: addl $127, %edi
75+
; CHECK-NEXT: jo .LBB2_3
76+
; CHECK-NEXT: # %bb.1: # %cont
77+
; CHECK-NEXT: addl $129, %esi
78+
; CHECK-NEXT: jo .LBB2_4
79+
; CHECK-NEXT: # %bb.2: # %cont2
80+
; CHECK-NEXT: cmpl %esi, %edi
81+
; CHECK-NEXT: cmovll %edi, %esi
82+
; CHECK-NEXT: movl %esi, %eax
83+
; CHECK-NEXT: retq
84+
; CHECK-NEXT: .LBB2_3: # %trap
85+
; CHECK-NEXT: ud1l 2(%eax), %eax
86+
; CHECK-NEXT: .LBB2_4: # %trap1
87+
; CHECK-NEXT: ud1l 4(%eax), %eax
88+
entry:
89+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 127), !nosanitize !5
90+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
91+
br i1 %1, label %trap, label %cont, !nosanitize !5
92+
93+
trap: ; preds = %entry
94+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
95+
unreachable, !nosanitize !5
96+
97+
cont: ; preds = %entry
98+
%2 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %y, i32 129), !nosanitize !5
99+
%3 = extractvalue { i32, i1 } %2, 1, !nosanitize !5
100+
br i1 %3, label %trap1, label %cont2, !nosanitize !5
101+
102+
trap1: ; preds = %cont
103+
tail call void @llvm.ubsantrap(i8 4) #4, !nosanitize !5
104+
unreachable, !nosanitize !5
105+
106+
cont2: ; preds = %cont
107+
%4 = extractvalue { i32, i1 } %2, 0, !nosanitize !5
108+
%5 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
109+
%cond = tail call i32 @llvm.smin.i32(i32 %5, i32 %4)
110+
ret i32 %cond
111+
}
112+
113+
; Function Attrs: nounwind uwtable
114+
define dso_local noundef i32 @m(i32 noundef %x, i32 noundef %y) local_unnamed_addr #0 {
115+
; CHECK-LABEL: m:
116+
; CHECK: # %bb.0: # %entry
117+
; CHECK-NEXT: addl $125, %edi
118+
; CHECK-NEXT: jo .LBB3_4
119+
; CHECK-NEXT: # %bb.1: # %f.exit
120+
; CHECK-NEXT: addl $127, %esi
121+
; CHECK-NEXT: jo .LBB3_4
122+
; CHECK-NEXT: # %bb.2: # %g.exit
123+
; CHECK-NEXT: addl %esi, %edi
124+
; CHECK-NEXT: jo .LBB3_4
125+
; CHECK-NEXT: # %bb.3: # %cont
126+
; CHECK-NEXT: movl %edi, %eax
127+
; CHECK-NEXT: retq
128+
; CHECK-NEXT: .LBB3_4: # %trap.i
129+
; CHECK-NEXT: ud1l 2(%eax), %eax
130+
entry:
131+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 125), !nosanitize !5
132+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
133+
br i1 %1, label %trap.i, label %f.exit, !nosanitize !5
134+
135+
trap.i: ; preds = %entry
136+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
137+
unreachable, !nosanitize !5
138+
139+
f.exit: ; preds = %entry
140+
%2 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %y, i32 127), !nosanitize !5
141+
%3 = extractvalue { i32, i1 } %2, 1, !nosanitize !5
142+
br i1 %3, label %trap.i2, label %g.exit, !nosanitize !5
143+
144+
trap.i2: ; preds = %f.exit
145+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
146+
unreachable, !nosanitize !5
147+
148+
g.exit: ; preds = %f.exit
149+
%4 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
150+
%5 = extractvalue { i32, i1 } %2, 0, !nosanitize !5
151+
%6 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %4, i32 %5), !nosanitize !5
152+
%7 = extractvalue { i32, i1 } %6, 1, !nosanitize !5
153+
br i1 %7, label %trap, label %cont, !nosanitize !5
154+
155+
trap: ; preds = %g.exit
156+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
157+
unreachable, !nosanitize !5
158+
159+
cont: ; preds = %g.exit
160+
%8 = extractvalue { i32, i1 } %6, 0, !nosanitize !5
161+
ret i32 %8
162+
}
163+
164+
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
165+
declare i32 @llvm.smin.i32(i32, i32) #3
166+
167+
attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
168+
attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
169+
attributes #2 = { cold noreturn nounwind }
170+
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
171+
attributes #4 = { noreturn nounwind }
172+
173+
!llvm.module.flags = !{!0, !1, !2, !3}
174+
!llvm.ident = !{!4}
175+
176+
!0 = !{i32 1, !"wchar_size", i32 4}
177+
!1 = !{i32 8, !"PIC Level", i32 2}
178+
!2 = !{i32 7, !"PIE Level", i32 2}
179+
!3 = !{i32 7, !"uwtable", i32 2}
180+
!4 = !{!"clang version 20.0.0git (https://github.com/llvm/llvm-project.git fe3c23b439b9a2d00442d9bc6a4ca86f73066a3d)"}
181+
!5 = !{}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
2+
; RUN: llc -O3 -mtriple x86_64 -filetype asm -o - %s | FileCheck %s
3+
;
4+
; This tests that the nomerge attribute for ubsantrap works correctly i.e.,
5+
; they are lowered to separate ud1 instructions.
6+
;
7+
; The LLVM IR was generated from clang/test/CodeGen/ubsan-trap-merge.c with
8+
; 'nomerge' manually added to ubsantraps.
9+
;
10+
; ModuleID = '../clang/test/CodeGen/ubsan-trap-merge.c'
11+
source_filename = "../clang/test/CodeGen/ubsan-trap-merge.c"
12+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
13+
target triple = "x86_64-unknown-linux-gnu"
14+
15+
; Function Attrs: nounwind uwtable
16+
define dso_local range(i32 -2147483523, -2147483648) i32 @f(i32 noundef %x) local_unnamed_addr #0 {
17+
; CHECK-LABEL: f:
18+
; CHECK: # %bb.0: # %entry
19+
; CHECK-NEXT: addl $125, %edi
20+
; CHECK-NEXT: jo .LBB0_1
21+
; CHECK-NEXT: # %bb.2: # %cont
22+
; CHECK-NEXT: movl %edi, %eax
23+
; CHECK-NEXT: retq
24+
; CHECK-NEXT: .LBB0_1: # %trap
25+
; CHECK-NEXT: ud1l 2(%eax), %eax
26+
entry:
27+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 125), !nosanitize !5
28+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
29+
br i1 %1, label %trap, label %cont, !nosanitize !5
30+
31+
trap: ; preds = %entry
32+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
33+
unreachable, !nosanitize !5
34+
35+
cont: ; preds = %entry
36+
%2 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
37+
ret i32 %2
38+
}
39+
40+
; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
41+
declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #1
42+
43+
; Function Attrs: cold noreturn nounwind
44+
declare void @llvm.ubsantrap(i8 immarg) #2
45+
46+
; Function Attrs: nounwind uwtable
47+
define dso_local range(i32 -2147483521, -2147483648) i32 @g(i32 noundef %x) local_unnamed_addr #0 {
48+
; CHECK-LABEL: g:
49+
; CHECK: # %bb.0: # %entry
50+
; CHECK-NEXT: addl $127, %edi
51+
; CHECK-NEXT: jo .LBB1_1
52+
; CHECK-NEXT: # %bb.2: # %cont
53+
; CHECK-NEXT: movl %edi, %eax
54+
; CHECK-NEXT: retq
55+
; CHECK-NEXT: .LBB1_1: # %trap
56+
; CHECK-NEXT: ud1l 2(%eax), %eax
57+
entry:
58+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 127), !nosanitize !5
59+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
60+
br i1 %1, label %trap, label %cont, !nosanitize !5
61+
62+
trap: ; preds = %entry
63+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
64+
unreachable, !nosanitize !5
65+
66+
cont: ; preds = %entry
67+
%2 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
68+
ret i32 %2
69+
}
70+
71+
; Function Attrs: nounwind uwtable
72+
define dso_local range(i32 -2147483521, -2147483648) i32 @h(i32 noundef %x, i32 noundef %y) local_unnamed_addr #0 {
73+
; CHECK-LABEL: h:
74+
; CHECK: # %bb.0: # %entry
75+
; CHECK-NEXT: addl $127, %edi
76+
; CHECK-NEXT: jo .LBB2_3
77+
; CHECK-NEXT: # %bb.1: # %cont
78+
; CHECK-NEXT: addl $129, %esi
79+
; CHECK-NEXT: jo .LBB2_4
80+
; CHECK-NEXT: # %bb.2: # %cont2
81+
; CHECK-NEXT: cmpl %esi, %edi
82+
; CHECK-NEXT: cmovll %edi, %esi
83+
; CHECK-NEXT: movl %esi, %eax
84+
; CHECK-NEXT: retq
85+
; CHECK-NEXT: .LBB2_3: # %trap
86+
; CHECK-NEXT: ud1l 2(%eax), %eax
87+
; CHECK-NEXT: .LBB2_4: # %trap1
88+
; CHECK-NEXT: ud1l 4(%eax), %eax
89+
entry:
90+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 127), !nosanitize !5
91+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
92+
br i1 %1, label %trap, label %cont, !nosanitize !5
93+
94+
trap: ; preds = %entry
95+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
96+
unreachable, !nosanitize !5
97+
98+
cont: ; preds = %entry
99+
%2 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %y, i32 129), !nosanitize !5
100+
%3 = extractvalue { i32, i1 } %2, 1, !nosanitize !5
101+
br i1 %3, label %trap1, label %cont2, !nosanitize !5
102+
103+
trap1: ; preds = %cont
104+
tail call void @llvm.ubsantrap(i8 4) #4, !nosanitize !5
105+
unreachable, !nosanitize !5
106+
107+
cont2: ; preds = %cont
108+
%4 = extractvalue { i32, i1 } %2, 0, !nosanitize !5
109+
%5 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
110+
%cond = tail call i32 @llvm.smin.i32(i32 %5, i32 %4)
111+
ret i32 %cond
112+
}
113+
114+
; Function Attrs: nounwind uwtable
115+
define dso_local noundef i32 @m(i32 noundef %x, i32 noundef %y) local_unnamed_addr #0 {
116+
; CHECK-LABEL: m:
117+
; CHECK: # %bb.0: # %entry
118+
; CHECK-NEXT: addl $125, %edi
119+
; CHECK-NEXT: jo .LBB3_4
120+
; CHECK-NEXT: # %bb.1: # %f.exit
121+
; CHECK-NEXT: addl $127, %esi
122+
; CHECK-NEXT: jo .LBB3_5
123+
; CHECK-NEXT: # %bb.2: # %g.exit
124+
; CHECK-NEXT: addl %esi, %edi
125+
; CHECK-NEXT: jo .LBB3_6
126+
; CHECK-NEXT: # %bb.3: # %cont
127+
; CHECK-NEXT: movl %edi, %eax
128+
; CHECK-NEXT: retq
129+
; CHECK-NEXT: .LBB3_4: # %trap.i
130+
; CHECK-NEXT: ud1l 2(%eax), %eax
131+
; CHECK-NEXT: .LBB3_5: # %trap.i2
132+
; CHECK-NEXT: ud1l 2(%eax), %eax
133+
; CHECK-NEXT: .LBB3_6: # %trap
134+
; CHECK-NEXT: ud1l 2(%eax), %eax
135+
entry:
136+
%0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 125), !nosanitize !5
137+
%1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
138+
br i1 %1, label %trap.i, label %f.exit, !nosanitize !5
139+
140+
trap.i: ; preds = %entry
141+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
142+
unreachable, !nosanitize !5
143+
144+
f.exit: ; preds = %entry
145+
%2 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %y, i32 127), !nosanitize !5
146+
%3 = extractvalue { i32, i1 } %2, 1, !nosanitize !5
147+
br i1 %3, label %trap.i2, label %g.exit, !nosanitize !5
148+
149+
trap.i2: ; preds = %f.exit
150+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
151+
unreachable, !nosanitize !5
152+
153+
g.exit: ; preds = %f.exit
154+
%4 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
155+
%5 = extractvalue { i32, i1 } %2, 0, !nosanitize !5
156+
%6 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %4, i32 %5), !nosanitize !5
157+
%7 = extractvalue { i32, i1 } %6, 1, !nosanitize !5
158+
br i1 %7, label %trap, label %cont, !nosanitize !5
159+
160+
trap: ; preds = %g.exit
161+
tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
162+
unreachable, !nosanitize !5
163+
164+
cont: ; preds = %g.exit
165+
%8 = extractvalue { i32, i1 } %6, 0, !nosanitize !5
166+
ret i32 %8
167+
}
168+
169+
; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
170+
declare i32 @llvm.smin.i32(i32, i32) #3
171+
172+
attributes #0 = { nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
173+
attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
174+
attributes #2 = { cold noreturn nounwind }
175+
attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
176+
attributes #4 = { nomerge noreturn nounwind }
177+
178+
!llvm.module.flags = !{!0, !1, !2, !3}
179+
!llvm.ident = !{!4}
180+
181+
!0 = !{i32 1, !"wchar_size", i32 4}
182+
!1 = !{i32 8, !"PIC Level", i32 2}
183+
!2 = !{i32 7, !"PIE Level", i32 2}
184+
!3 = !{i32 7, !"uwtable", i32 2}
185+
!4 = !{!"clang version 20.0.0git (https://github.com/llvm/llvm-project.git fe3c23b439b9a2d00442d9bc6a4ca86f73066a3d)"}
186+
!5 = !{}

0 commit comments

Comments
 (0)