1 change: 1 addition & 0 deletions llvm/test/CodeGen/AArch64/O3-pipeline.ll
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
; CHECK-NEXT: Interleaved Load Combine Pass
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Interleaved Access Pass
; CHECK-NEXT: AArch64 Stack Tagging
; CHECK-NEXT: Natural Loop Information
; CHECK-NEXT: CodeGen Prepare
; CHECK-NEXT: Rewrite Symbols
Expand Down
37 changes: 37 additions & 0 deletions llvm/test/CodeGen/AArch64/stack-tagging-dbg.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
; RUN: opt < %s -stack-tagging -S -o - | FileCheck %s

target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-android"

declare void @use32(i32*)
declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone speculatable

; Debug intrinsics use the new alloca directly, not through a GEP or a tagp.
define void @DbgIntrinsics() sanitize_memtag {
entry:
%x = alloca i32, align 4
call void @llvm.dbg.declare(metadata i32* %x, metadata !6, metadata !DIExpression()), !dbg !10
store i32 42, i32* %x, align 4
call void @use32(i32* %x)
ret void
}

; CHECK-LABEL: define void @DbgIntrinsics(
; CHECK: [[X:%.*]] = alloca { i32, [12 x i8] }, align 16
; CHECK: call void @llvm.dbg.declare(metadata { i32, [12 x i8] }* [[X]],


!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!8, !9}

!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 9.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
!1 = !DIFile(filename: "stack-tagging.cc", directory: "/tmp")
!2 = !{}
!3 = distinct !DISubprogram(name: "DbgIntrinsics", linkageName: "DbgIntrinsics", scope: !1, file: !1, line: 3, type: !4, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
!4 = !DISubroutineType(types: !5)
!5 = !{null}
!6 = !DILocalVariable(name: "x", scope: !3, file: !1, line: 4, type: !7)
!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!8 = !{i32 2, !"Dwarf Version", i32 4}
!9 = !{i32 2, !"Debug Info Version", i32 3}
!10 = !DILocation(line: 1, column: 2, scope: !3)
187 changes: 187 additions & 0 deletions llvm/test/CodeGen/AArch64/stack-tagging.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
; RUN: opt < %s -stack-tagging -S -o - | FileCheck %s

target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-android"

declare void @use8(i8*)
declare void @use32(i32*)
declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture)
declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture)

define void @OneVar() sanitize_memtag {
entry:
%x = alloca i32, align 4
call void @use32(i32* %x)
ret void
}

; CHECK-LABEL: define void @OneVar(
; CHECK: [[BASE:%.*]] = call i8* @llvm.aarch64.irg.sp(i64 0)
; CHECK: [[X:%.*]] = alloca { i32, [12 x i8] }, align 16
; CHECK: [[TX:%.*]] = call { i32, [12 x i8] }* @llvm.aarch64.tagp.{{.*}}({ i32, [12 x i8] }* [[X]], i8* [[BASE]], i64 0)
; CHECK: [[TX8:%.*]] = bitcast { i32, [12 x i8] }* [[TX]] to i8*
; CHECK: call void @llvm.aarch64.settag(i8* [[TX8]], i64 16)
; CHECK: [[GEP32:%.*]] = bitcast { i32, [12 x i8] }* [[TX]] to i32*
; CHECK: call void @use32(i32* [[GEP32]])
; CHECK: [[GEP8:%.*]] = bitcast { i32, [12 x i8] }* [[X]] to i8*
; CHECK: call void @llvm.aarch64.settag(i8* [[GEP8]], i64 16)
; CHECK: ret void


define void @ManyVars() sanitize_memtag {
entry:
%x1 = alloca i32, align 4
%x2 = alloca i8, align 4
%x3 = alloca i32, i32 11, align 4
call void @use32(i32* %x1)
call void @use8(i8* %x2)
call void @use32(i32* %x3)
ret void
}

; CHECK-LABEL: define void @ManyVars(
; CHECK: alloca { i32, [12 x i8] }, align 16
; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp.{{.*}}({ i32, [12 x i8] }* {{.*}}, i64 0)
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
; CHECK: alloca { i8, [15 x i8] }, align 16
; CHECK: call { i8, [15 x i8] }* @llvm.aarch64.tagp.{{.*}}({ i8, [15 x i8] }* {{.*}}, i64 1)
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
; CHECK: alloca { [11 x i32], [4 x i8] }, align 16
; CHECK: call { [11 x i32], [4 x i8] }* @llvm.aarch64.tagp.{{.*}}({ [11 x i32], [4 x i8] }* {{.*}}, i64 2)
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 48)

; CHECK: call void @use32(
; CHECK: call void @use8(
; CHECK: call void @use32(

; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 48)
; CHECK-NEXT: ret void


define void @Scope(i32 %b) sanitize_memtag {
entry:
%x = alloca i32, align 4
%tobool = icmp eq i32 %b, 0
br i1 %tobool, label %if.end, label %if.then

if.then:
%0 = bitcast i32* %x to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
call void @use8(i8* %0) #3
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)
br label %if.end

if.end:
ret void
}

; CHECK-LABEL: define void @Scope(
; CHECK: br i1
; CHECK: call void @llvm.lifetime.start.p0i8(
; CHECK: call void @llvm.aarch64.settag(
; CHECK: call void @use8(
; CHECK: call void @llvm.aarch64.settag(
; CHECK: call void @llvm.lifetime.end.p0i8(
; CHECK: br label
; CHECK: ret void


; Spooked by the multiple lifetime ranges, StackTagging remove all of them and sets tags on entry and exit.
define void @BadScope(i32 %b) sanitize_memtag {
entry:
%x = alloca i32, align 4
%tobool = icmp eq i32 %b, 0
br i1 %tobool, label %if.end, label %if.then

if.then:
%0 = bitcast i32* %x to i8*
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
call void @use8(i8* %0) #3
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)

call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0)
call void @use8(i8* %0) #3
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0)
br label %if.end

if.end:
ret void
}

; CHECK-LABEL: define void @BadScope(
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
; CHECK: br i1
; CHECK: call void @use8(i8*
; CHECK-NEXT: call void @use8(i8*
; CHECK: br label
; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16)
; CHECK-NEXT: ret void

define void @DynamicAllocas(i32 %cnt) sanitize_memtag {
entry:
%x = alloca i32, i32 %cnt, align 4
br label %l
l:
%y = alloca i32, align 4
call void @use32(i32* %x)
call void @use32(i32* %y)
ret void
}

; CHECK-LABEL: define void @DynamicAllocas(
; CHECK-NOT: @llvm.aarch64.irg.sp
; CHECK: %x = alloca i32, i32 %cnt, align 4
; CHECK-NOT: @llvm.aarch64.irg.sp
; CHECK: alloca i32, align 4
; CHECK-NOT: @llvm.aarch64.irg.sp
; CHECK: ret void

; If we can't trace one of the lifetime markers to a single alloca, fall back
; to poisoning all allocas at the beginning of the function.
; Each alloca must be poisoned only once.
define void @UnrecognizedLifetime(i8 %v) sanitize_memtag {
entry:
%x = alloca i32, align 4
%y = alloca i32, align 4
%z = alloca i32, align 4
%cx = bitcast i32* %x to i8*
%cy = bitcast i32* %y to i8*
%cz = bitcast i32* %z to i8*
%tobool = icmp eq i8 %v, 0
%xy = select i1 %tobool, i32* %x, i32* %y
%cxcy = select i1 %tobool, i8* %cx, i8* %cy
br label %another_bb

another_bb:
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz)
store i32 7, i32* %z
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz)
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz)
store i32 7, i32* %z
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz)
call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cxcy)
store i32 8, i32* %xy
call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cxcy)
ret void
}

; CHECK-LABEL: define void @UnrecognizedLifetime(
; CHECK: call i8* @llvm.aarch64.irg.sp(i64 0)
; CHECK: alloca { i32, [12 x i8] }, align 16
; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp
; CHECK: call void @llvm.aarch64.settag(
; CHECK: alloca { i32, [12 x i8] }, align 16
; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp
; CHECK: call void @llvm.aarch64.settag(
; CHECK: alloca { i32, [12 x i8] }, align 16
; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp
; CHECK: call void @llvm.aarch64.settag(
; CHECK: store i32
; CHECK: store i32
; CHECK: store i32
; CHECK: call void @llvm.aarch64.settag(
; CHECK: call void @llvm.aarch64.settag(
; CHECK: call void @llvm.aarch64.settag(
; CHECK: ret void