From fd0154041b72a25f84a20d8304938a3655ae2aa1 Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Thu, 16 Jun 2016 22:34:00 +0000 Subject: [PATCH] [safestack] Fixup llvm.dbg.value when rewriting unsafe allocas. When moving unsafe allocas to the unsafe stack, dbg.declare intrinsics are updated to refer to the new location. This change does the same to dbg.value intrinsics. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@272968 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Transforms/Utils/Local.h | 8 ++ lib/CodeGen/SafeStack.cpp | 1 + lib/Transforms/Utils/Local.cpp | 92 +++++++++++++---- .../Transforms/SafeStack/debug-loc-dynamic.ll | 57 +++++++++++ test/Transforms/SafeStack/debug-loc2.ll | 98 +++++++++++++++++++ 5 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 test/Transforms/SafeStack/debug-loc-dynamic.ll create mode 100644 test/Transforms/SafeStack/debug-loc2.ll diff --git a/include/llvm/Transforms/Utils/Local.h b/include/llvm/Transforms/Utils/Local.h index f932b684dc5..dd4717bcd06 100644 --- a/include/llvm/Transforms/Utils/Local.h +++ b/include/llvm/Transforms/Utils/Local.h @@ -281,6 +281,14 @@ bool replaceDbgDeclare(Value *Address, Value *NewAddress, bool replaceDbgDeclareForAlloca(AllocaInst *AI, Value *NewAllocaAddress, DIBuilder &Builder, bool Deref, int Offset = 0); +/// Replaces multiple llvm.dbg.value instructions when the alloca it describes +/// is replaced with a new value. If Offset is non-zero, a constant displacement +/// is added to the expression (after the mandatory Deref). Offset can be +/// negative. New llvm.dbg.value instructions are inserted at the locations of +/// the instructions they replace. +void replaceDbgValueForAlloca(AllocaInst *AI, Value *NewAllocaAddress, + DIBuilder &Builder, int Offset = 0); + /// Remove all instructions from a basic block other than it's terminator /// and any present EH pad instructions. unsigned removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB); diff --git a/lib/CodeGen/SafeStack.cpp b/lib/CodeGen/SafeStack.cpp index 1a6635f5714..28f5ed861ab 100644 --- a/lib/CodeGen/SafeStack.cpp +++ b/lib/CodeGen/SafeStack.cpp @@ -614,6 +614,7 @@ Value *SafeStack::moveStaticAllocasToUnsafeStack( // Replace alloc with the new location. replaceDbgDeclareForAlloca(AI, BasePointer, DIB, /*Deref=*/true, -StaticOffset); + replaceDbgValueForAlloca(AI, BasePointer, DIB, -StaticOffset); AI->replaceAllUsesWith(NewAI); AI->eraseFromParent(); } diff --git a/lib/Transforms/Utils/Local.cpp b/lib/Transforms/Utils/Local.cpp index edde166b1ed..a0dd5d1a434 100644 --- a/lib/Transforms/Utils/Local.cpp +++ b/lib/Transforms/Utils/Local.cpp @@ -1182,6 +1182,38 @@ DbgDeclareInst *llvm::FindAllocaDbgDeclare(Value *V) { return nullptr; } +static void DIExprAddDeref(SmallVectorImpl &Expr) { + Expr.push_back(dwarf::DW_OP_deref); +} + +static void DIExprAddOffset(SmallVectorImpl &Expr, int Offset) { + if (Offset > 0) { + Expr.push_back(dwarf::DW_OP_plus); + Expr.push_back(Offset); + } else if (Offset < 0) { + Expr.push_back(dwarf::DW_OP_minus); + Expr.push_back(-Offset); + } +} + +static DIExpression *BuildReplacementDIExpr(DIBuilder &Builder, + DIExpression *DIExpr, bool Deref, + int Offset) { + if (!Deref && !Offset) + return DIExpr; + // Create a copy of the original DIDescriptor for user variable, prepending + // "deref" operation to a list of address elements, as new llvm.dbg.declare + // will take a value storing address of the memory for variable, not + // alloca itself. + SmallVector NewDIExpr; + if (Deref) + DIExprAddDeref(NewDIExpr); + DIExprAddOffset(NewDIExpr, Offset); + if (DIExpr) + NewDIExpr.append(DIExpr->elements_begin(), DIExpr->elements_end()); + return Builder.createExpression(NewDIExpr); +} + bool llvm::replaceDbgDeclare(Value *Address, Value *NewAddress, Instruction *InsertBefore, DIBuilder &Builder, bool Deref, int Offset) { @@ -1193,25 +1225,7 @@ bool llvm::replaceDbgDeclare(Value *Address, Value *NewAddress, auto *DIExpr = DDI->getExpression(); assert(DIVar && "Missing variable"); - if (Deref || Offset) { - // Create a copy of the original DIDescriptor for user variable, prepending - // "deref" operation to a list of address elements, as new llvm.dbg.declare - // will take a value storing address of the memory for variable, not - // alloca itself. - SmallVector NewDIExpr; - if (Deref) - NewDIExpr.push_back(dwarf::DW_OP_deref); - if (Offset > 0) { - NewDIExpr.push_back(dwarf::DW_OP_plus); - NewDIExpr.push_back(Offset); - } else if (Offset < 0) { - NewDIExpr.push_back(dwarf::DW_OP_minus); - NewDIExpr.push_back(-Offset); - } - if (DIExpr) - NewDIExpr.append(DIExpr->elements_begin(), DIExpr->elements_end()); - DIExpr = Builder.createExpression(NewDIExpr); - } + DIExpr = BuildReplacementDIExpr(Builder, DIExpr, Deref, Offset); // Insert llvm.dbg.declare immediately after the original alloca, and remove // old llvm.dbg.declare. @@ -1226,6 +1240,46 @@ bool llvm::replaceDbgDeclareForAlloca(AllocaInst *AI, Value *NewAllocaAddress, Deref, Offset); } +static void replaceOneDbgValueForAlloca(DbgValueInst *DVI, Value *NewAddress, + DIBuilder &Builder, int Offset) { + DebugLoc Loc = DVI->getDebugLoc(); + auto *DIVar = DVI->getVariable(); + auto *DIExpr = DVI->getExpression(); + assert(DIVar && "Missing variable"); + + // This is an alloca-based llvm.dbg.value. The first thing it should do with + // the alloca pointer is dereference it. Otherwise we don't know how to handle + // it and give up. + if (!DIExpr || DIExpr->getNumElements() < 1 || + DIExpr->getElement(0) != dwarf::DW_OP_deref) + return; + + // Insert the offset immediately after the first deref. + // We could just change the offset argument of dbg.value, but it's unsigned... + if (Offset) { + SmallVector NewDIExpr; + DIExprAddDeref(NewDIExpr); + DIExprAddOffset(NewDIExpr, Offset); + NewDIExpr.append(DIExpr->elements_begin() + 1, DIExpr->elements_end()); + DIExpr = Builder.createExpression(NewDIExpr); + } + + Builder.insertDbgValueIntrinsic(NewAddress, DVI->getOffset(), DIVar, DIExpr, + Loc, DVI); + DVI->eraseFromParent(); +} + +void llvm::replaceDbgValueForAlloca(AllocaInst *AI, Value *NewAllocaAddress, + DIBuilder &Builder, int Offset) { + if (auto *L = LocalAsMetadata::getIfExists(AI)) + if (auto *MDV = MetadataAsValue::getIfExists(AI->getContext(), L)) + for (auto UI = MDV->use_begin(), UE = MDV->use_end(); UI != UE;) { + Use &U = *UI++; + if (auto *DVI = dyn_cast(U.getUser())) + replaceOneDbgValueForAlloca(DVI, NewAllocaAddress, Builder, Offset); + } +} + unsigned llvm::removeAllNonTerminatorAndEHPadInstructions(BasicBlock *BB) { unsigned NumDeadInst = 0; // Delete the instructions backwards, as it has a reduced likelihood of diff --git a/test/Transforms/SafeStack/debug-loc-dynamic.ll b/test/Transforms/SafeStack/debug-loc-dynamic.ll new file mode 100644 index 00000000000..280d010774e --- /dev/null +++ b/test/Transforms/SafeStack/debug-loc-dynamic.ll @@ -0,0 +1,57 @@ +; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s + +; Test llvm.dbg.value for dynamic allocas moved onto the unsafe stack. +; In the dynamic alloca case, the dbg.value does not change with the exception +; of the alloca pointer in the first argument being replaced with the new stack +; top address. + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @f(i32 %n) safestack !dbg !6 { +entry: + tail call void @llvm.dbg.value(metadata i32 %n, i64 0, metadata !11, metadata !14), !dbg !15 + %0 = zext i32 %n to i64, !dbg !16 + +; CHECK: store i8* %[[VLA:.*]], i8** @__safestack_unsafe_stack_ptr +; CHECK: tail call void @llvm.dbg.value(metadata i8* %[[VLA]], i64 0, metadata ![[TYPE:.*]], metadata ![[EXPR:.*]]) +; CHECK: call void @capture({{.*}} %[[VLA]]) + + %vla = alloca i8, i64 %0, align 16, !dbg !16 + tail call void @llvm.dbg.value(metadata i8* %vla, i64 0, metadata !12, metadata !17), !dbg !18 + call void @capture(i8* nonnull %vla), !dbg !19 + ret void, !dbg !20 +} + +declare void @capture(i8*) +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.9.0 (trunk 272832) (llvm/trunk 272831)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "../llvm/1.cc", directory: "/code/build-llvm") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{!"clang version 3.9.0 (trunk 272832) (llvm/trunk 272831)"} +!6 = distinct !DISubprogram(name: "f", linkageName: "_Z1fi", scope: !1, file: !1, line: 2, type: !7, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !10) +!7 = !DISubroutineType(types: !8) +!8 = !{null, !9} +!9 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!10 = !{!11, !12} +!11 = !DILocalVariable(name: "n", arg: 1, scope: !6, file: !1, line: 2, type: !9) + +; CHECK-DAG: ![[TYPE]] = !DILocalVariable(name: "x", +!12 = !DILocalVariable(name: "x", scope: !6, file: !1, line: 3, type: !13) +!13 = !DIBasicType(name: "char", size: 8, align: 8, encoding: DW_ATE_signed_char) +!14 = !DIExpression() +!15 = !DILocation(line: 2, column: 12, scope: !6) +!16 = !DILocation(line: 3, column: 3, scope: !6) + +; CHECK-DAG: ![[EXPR]] = !DIExpression(DW_OP_deref) +!17 = !DIExpression(DW_OP_deref) +!18 = !DILocation(line: 3, column: 8, scope: !6) +!19 = !DILocation(line: 4, column: 3, scope: !6) +!20 = !DILocation(line: 5, column: 1, scope: !6) diff --git a/test/Transforms/SafeStack/debug-loc2.ll b/test/Transforms/SafeStack/debug-loc2.ll new file mode 100644 index 00000000000..35e9b7711d2 --- /dev/null +++ b/test/Transforms/SafeStack/debug-loc2.ll @@ -0,0 +1,98 @@ +; RUN: opt -safe-stack -S -mtriple=i386-pc-linux-gnu < %s -o - | FileCheck %s + +; Test llvm.dbg.value for the local variables moved onto the unsafe stack. +; SafeStack rewrites them relative to the unsafe stack pointer (base address of +; the unsafe stack frame). + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: noinline safestack uwtable +define void @f() #0 !dbg !6 { +entry: +; CHECK: %[[USP:.*]] = load i8*, i8** @__safestack_unsafe_stack_ptr + %x1 = alloca i32, align 4 + %x2 = alloca i32, align 4 + %0 = bitcast i32* %x1 to i8*, !dbg !13 + %1 = bitcast i32* %x2 to i8*, !dbg !14 + +; Unhandled dbg.value: expression does not start with OP_DW_deref +; CHECK: call void @llvm.dbg.value(metadata ![[EMPTY:.*]], i64 0, metadata !{{.*}}, metadata !{{.*}}) + tail call void @llvm.dbg.value(metadata i32* %x1, i64 0, metadata !10, metadata !23), !dbg !16 + +; Unhandled dbg.value: expression does not start with OP_DW_deref +; CHECK: call void @llvm.dbg.value(metadata ![[EMPTY]], i64 0, metadata !{{.*}}, metadata !{{.*}}) + tail call void @llvm.dbg.value(metadata i32* %x1, i64 0, metadata !10, metadata !24), !dbg !16 + +; Supported dbg.value: rewritted based on the [[USP]] value. +; CHECK: call void @llvm.dbg.value(metadata i8* %[[USP]], i64 0, metadata ![[X1:.*]], metadata ![[X1_EXPR:.*]]) + tail call void @llvm.dbg.value(metadata i32* %x1, i64 0, metadata !10, metadata !15), !dbg !16 + call void @capture(i32* nonnull %x1), !dbg !17 + +; An extra non-dbg.value metadata use of %x2. Replaced with an empty metadata. +; CHECK: call void @llvm.random.metadata.use(metadata ![[EMPTY]]) + call void @llvm.random.metadata.use(metadata i32* %x2) + +; CHECK: call void @llvm.dbg.value(metadata i8* %[[USP]], i64 0, metadata ![[X2:.*]], metadata ![[X2_EXPR:.*]]) + call void @llvm.dbg.value(metadata i32* %x2, i64 0, metadata !12, metadata !15), !dbg !18 + call void @capture(i32* nonnull %x2), !dbg !19 + ret void, !dbg !20 +} + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.start(i64, i8* nocapture) #1 + +declare void @capture(i32*) #2 + +; Function Attrs: argmemonly nounwind +declare void @llvm.lifetime.end(i64, i8* nocapture) #1 + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #3 + +declare void @llvm.random.metadata.use(metadata) + +attributes #0 = { noinline safestack uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { argmemonly nounwind } +attributes #2 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #3 = { nounwind readnone } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4} +!llvm.ident = !{!5} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.9.0 (trunk 271022) (llvm/trunk 271027)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "../llvm/2.cc", directory: "/code/build-llvm") + +; CHECK-DAG: ![[EMPTY]] = !{} +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{!"clang version 3.9.0 (trunk 271022) (llvm/trunk 271027)"} +!6 = distinct !DISubprogram(name: "f", linkageName: "_Z1fv", scope: !1, file: !1, line: 4, type: !7, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !9) +!7 = !DISubroutineType(types: !8) +!8 = !{null} +!9 = !{!10, !12} + +; CHECK-DAG: ![[X1]] = !DILocalVariable(name: "x1", +!10 = !DILocalVariable(name: "x1", scope: !6, file: !1, line: 5, type: !11) +!11 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) + +; CHECK-DAG: ![[X2]] = !DILocalVariable(name: "x2", +!12 = !DILocalVariable(name: "x2", scope: !6, file: !1, line: 6, type: !11) +!13 = !DILocation(line: 5, column: 3, scope: !6) +!14 = !DILocation(line: 6, column: 3, scope: !6) + +; CHECK-DAG: ![[X1_EXPR]] = !DIExpression(DW_OP_deref, DW_OP_minus, 4) +; CHECK-DAG: ![[X2_EXPR]] = !DIExpression(DW_OP_deref, DW_OP_minus, 8) +!15 = !DIExpression(DW_OP_deref) +!16 = !DILocation(line: 5, column: 7, scope: !6) +!17 = !DILocation(line: 8, column: 3, scope: !6) +!18 = !DILocation(line: 6, column: 7, scope: !6) +!19 = !DILocation(line: 9, column: 3, scope: !6) +!20 = !DILocation(line: 10, column: 1, scope: !6) +!21 = !DILocation(line: 10, column: 1, scope: !22) +!22 = !DILexicalBlockFile(scope: !6, file: !1, discriminator: 1) +!23 = !DIExpression() +!24 = !DIExpression(DW_OP_minus, 42) -- 2.50.1