Skip to content

[NFC][clang] Add ubsan-trap-merge.ll test to show absence of nomerge considered harmful #117657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Nov 26, 2024

Conversation

thurstond
Copy link
Contributor

@thurstond thurstond commented Nov 26, 2024

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.

…considered harmful

This testcase demonstrates that when nomerge is missing from ubsantrap
intrinsics, they may be merged when inlining.

This is based on the observation and testcase by Vitaly Buka in llvm#83470
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-mc

@llvm/pr-subscribers-backend-x86

Author: Thurston Dang (thurstond)

Changes

This testcase demonstrates that when nomerge is missing from ubsantrap intrinsics, they may be merged when inlining.

This is based on the observation and testcase by Vitaly Buka in #83470


Full diff: https://github.com/llvm/llvm-project/pull/117657.diff

1 Files Affected:

  • (added) llvm/test/MC/X86/ubsan-trap-merge.ll (+181)
diff --git a/llvm/test/MC/X86/ubsan-trap-merge.ll b/llvm/test/MC/X86/ubsan-trap-merge.ll
new file mode 100644
index 00000000000000..b96c85473e5387
--- /dev/null
+++ b/llvm/test/MC/X86/ubsan-trap-merge.ll
@@ -0,0 +1,181 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -O3 -mtriple x86_64 -filetype asm -o - %s | FileCheck %s
+;
+; This test shows that ubsantrap can, in the absence of nomerge, be merged by
+; the backend into a single ud1 instruction (thus making debugging difficult).
+;
+; The LLVM IR was generated from clang/test/CodeGen/ubsan-trap-merge.c.
+;
+; ModuleID = '../clang/test/CodeGen/ubsan-trap-merge.c'
+source_filename = "../clang/test/CodeGen/ubsan-trap-merge.c"
+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"
+target triple = "x86_64-unknown-linux-gnu"
+
+; Function Attrs: nounwind uwtable
+define dso_local range(i32 -2147483523, -2147483648) i32 @f(i32 noundef %x) local_unnamed_addr #0 {
+; CHECK-LABEL: f:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addl $125, %edi
+; CHECK-NEXT:    jo .LBB0_1
+; CHECK-NEXT:  # %bb.2: # %cont
+; CHECK-NEXT:    movl %edi, %eax
+; CHECK-NEXT:    retq
+; CHECK-NEXT:  .LBB0_1: # %trap
+; CHECK-NEXT:    ud1l 2(%eax), %eax
+entry:
+  %0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 125), !nosanitize !5
+  %1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
+  br i1 %1, label %trap, label %cont, !nosanitize !5
+
+trap:                                             ; preds = %entry
+  tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+cont:                                             ; preds = %entry
+  %2 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
+  ret i32 %2
+}
+
+; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare { i32, i1 } @llvm.sadd.with.overflow.i32(i32, i32) #1
+
+; Function Attrs: cold noreturn nounwind
+declare void @llvm.ubsantrap(i8 immarg) #2
+
+; Function Attrs: nounwind uwtable
+define dso_local range(i32 -2147483521, -2147483648) i32 @g(i32 noundef %x) local_unnamed_addr #0 {
+; CHECK-LABEL: g:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addl $127, %edi
+; CHECK-NEXT:    jo .LBB1_1
+; CHECK-NEXT:  # %bb.2: # %cont
+; CHECK-NEXT:    movl %edi, %eax
+; CHECK-NEXT:    retq
+; CHECK-NEXT:  .LBB1_1: # %trap
+; CHECK-NEXT:    ud1l 2(%eax), %eax
+entry:
+  %0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 127), !nosanitize !5
+  %1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
+  br i1 %1, label %trap, label %cont, !nosanitize !5
+
+trap:                                             ; preds = %entry
+  tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+cont:                                             ; preds = %entry
+  %2 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
+  ret i32 %2
+}
+
+; Function Attrs: nounwind uwtable
+define dso_local range(i32 -2147483521, -2147483648) i32 @h(i32 noundef %x, i32 noundef %y) local_unnamed_addr #0 {
+; CHECK-LABEL: h:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addl $127, %edi
+; CHECK-NEXT:    jo .LBB2_3
+; CHECK-NEXT:  # %bb.1: # %cont
+; CHECK-NEXT:    addl $129, %esi
+; CHECK-NEXT:    jo .LBB2_4
+; CHECK-NEXT:  # %bb.2: # %cont2
+; CHECK-NEXT:    cmpl %esi, %edi
+; CHECK-NEXT:    cmovll %edi, %esi
+; CHECK-NEXT:    movl %esi, %eax
+; CHECK-NEXT:    retq
+; CHECK-NEXT:  .LBB2_3: # %trap
+; CHECK-NEXT:    ud1l 2(%eax), %eax
+; CHECK-NEXT:  .LBB2_4: # %trap1
+; CHECK-NEXT:    ud1l 4(%eax), %eax
+entry:
+  %0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 127), !nosanitize !5
+  %1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
+  br i1 %1, label %trap, label %cont, !nosanitize !5
+
+trap:                                             ; preds = %entry
+  tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+cont:                                             ; preds = %entry
+  %2 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %y, i32 129), !nosanitize !5
+  %3 = extractvalue { i32, i1 } %2, 1, !nosanitize !5
+  br i1 %3, label %trap1, label %cont2, !nosanitize !5
+
+trap1:                                            ; preds = %cont
+  tail call void @llvm.ubsantrap(i8 4) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+cont2:                                            ; preds = %cont
+  %4 = extractvalue { i32, i1 } %2, 0, !nosanitize !5
+  %5 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
+  %cond = tail call i32 @llvm.smin.i32(i32 %5, i32 %4)
+  ret i32 %cond
+}
+
+; Function Attrs: nounwind uwtable
+define dso_local noundef i32 @m(i32 noundef %x, i32 noundef %y) local_unnamed_addr #0 {
+; CHECK-LABEL: m:
+; CHECK:       # %bb.0: # %entry
+; CHECK-NEXT:    addl $125, %edi
+; CHECK-NEXT:    jo .LBB3_4
+; CHECK-NEXT:  # %bb.1: # %f.exit
+; CHECK-NEXT:    addl $127, %esi
+; CHECK-NEXT:    jo .LBB3_4
+; CHECK-NEXT:  # %bb.2: # %g.exit
+; CHECK-NEXT:    addl %esi, %edi
+; CHECK-NEXT:    jo .LBB3_4
+; CHECK-NEXT:  # %bb.3: # %cont
+; CHECK-NEXT:    movl %edi, %eax
+; CHECK-NEXT:    retq
+; CHECK-NEXT:  .LBB3_4: # %trap.i
+; CHECK-NEXT:    ud1l 2(%eax), %eax
+entry:
+  %0 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %x, i32 125), !nosanitize !5
+  %1 = extractvalue { i32, i1 } %0, 1, !nosanitize !5
+  br i1 %1, label %trap.i, label %f.exit, !nosanitize !5
+
+trap.i:                                           ; preds = %entry
+  tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+f.exit:                                           ; preds = %entry
+  %2 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %y, i32 127), !nosanitize !5
+  %3 = extractvalue { i32, i1 } %2, 1, !nosanitize !5
+  br i1 %3, label %trap.i2, label %g.exit, !nosanitize !5
+
+trap.i2:                                          ; preds = %f.exit
+  tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+g.exit:                                           ; preds = %f.exit
+  %4 = extractvalue { i32, i1 } %0, 0, !nosanitize !5
+  %5 = extractvalue { i32, i1 } %2, 0, !nosanitize !5
+  %6 = tail call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 %4, i32 %5), !nosanitize !5
+  %7 = extractvalue { i32, i1 } %6, 1, !nosanitize !5
+  br i1 %7, label %trap, label %cont, !nosanitize !5
+
+trap:                                             ; preds = %g.exit
+  tail call void @llvm.ubsantrap(i8 2) #4, !nosanitize !5
+  unreachable, !nosanitize !5
+
+cont:                                             ; preds = %g.exit
+  %8 = extractvalue { i32, i1 } %6, 0, !nosanitize !5
+  ret i32 %8
+}
+
+; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+declare i32 @llvm.smin.i32(i32, i32) #3
+
+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" }
+attributes #1 = { mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #2 = { cold noreturn nounwind }
+attributes #3 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+attributes #4 = { noreturn nounwind }
+
+!llvm.module.flags = !{!0, !1, !2, !3}
+!llvm.ident = !{!4}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 8, !"PIC Level", i32 2}
+!2 = !{i32 7, !"PIE Level", i32 2}
+!3 = !{i32 7, !"uwtable", i32 2}
+!4 = !{!"clang version 20.0.0git (https://github.com/llvm/llvm-project.git fe3c23b439b9a2d00442d9bc6a4ca86f73066a3d)"}
+!5 = !{}

@thurstond thurstond merged commit dde7f4d into llvm:main Nov 26, 2024
8 checks passed
thurstond added a commit that referenced this pull request Nov 27, 2024
…117651)

#65972 (continuation of
https://reviews.llvm.org/D148654) had considered adding nomerge to
ubsantrap, but did not proceed with that because of
#53011. Instead, it added a
counter (based on TrapBB->getParent()->size()) to each ubsantrap call.
However, this counter is not guaranteed to be unique after inlining, as
shown by #83470, which can
result in ubsantraps being merged by the backend.

#101549 has since fixed the
nomerge limitation ("It sets nomerge flag for the node if the
instruction has nomerge arrtibute."). This patch therefore takes
advantage of nomerge instead of using the counter, guaranteeing that the
ubsantraps are not merged.

This patch is equivalent to
#83470 but also adds nomerge
and updates tests (#117649:
ubsan-trap-merge.c; #117657:
ubsan-trap-merge.ll, ubsan-trap-nomerge.ll; catch-undef-behavior.c).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:X86 mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants