ychen updated this revision to Diff 407384. ychen marked an inline comment as done. ychen added a comment.
- Remove CodeViewDebug.cpp and the associated test. It was needed because the instrumented flag variables were scoped inside the associated method in the CodeView. The current patch puts them in the CU scope (which MSVC does). So the flag variable would never be decorated with qualified name. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D118428/new/ https://reviews.llvm.org/D118428 Files: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/CodeGenOptions.def clang/include/clang/Basic/DiagnosticDriverKinds.td clang/include/clang/Driver/Options.td clang/lib/CodeGen/BackendUtil.cpp clang/lib/Driver/ToolChains/Clang.cpp clang/test/Driver/cl-options.c llvm/include/llvm/CodeGen/CommandFlags.h llvm/include/llvm/CodeGen/Passes.h llvm/include/llvm/InitializePasses.h llvm/include/llvm/LinkAllPasses.h llvm/include/llvm/Target/TargetOptions.h llvm/lib/CodeGen/CMakeLists.txt llvm/lib/CodeGen/CodeGen.cpp llvm/lib/CodeGen/CommandFlags.cpp llvm/lib/CodeGen/JMCInstrumenter.cpp llvm/lib/Target/AArch64/AArch64TargetMachine.cpp llvm/lib/Target/ARM/ARMTargetMachine.cpp llvm/lib/Target/X86/X86TargetMachine.cpp llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll llvm/tools/opt/opt.cpp
Index: llvm/tools/opt/opt.cpp =================================================================== --- llvm/tools/opt/opt.cpp +++ llvm/tools/opt/opt.cpp @@ -498,7 +498,8 @@ "generic-to-nvvm", "expandmemcmp", "loop-reduce", "lower-amx-type", "pre-amx-config", "lower-amx-intrinsics", - "polyhedral-info", "replace-with-veclib"}; + "polyhedral-info", "replace-with-veclib", + "jmc-instrument"}; for (const auto &P : PassNamePrefix) if (Pass.startswith(P)) return true; @@ -572,6 +573,7 @@ initializeHardwareLoopsPass(Registry); initializeTypePromotionPass(Registry); initializeReplaceWithVeclibLegacyPass(Registry); + initializeJMCInstrumenterPass(Registry); #ifdef BUILD_EXAMPLES initializeExampleIRTransforms(Registry); Index: llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/JustMyCode/jmc-instrument.ll @@ -0,0 +1,120 @@ +; RUN: opt -jmc-instrument -mtriple=x86_64-pc-windows-msvc -S < %s | FileCheck %s +; RUN: opt -jmc-instrument -mtriple=aarch64-pc-windows-msvc -S < %s | FileCheck %s +; RUN: opt -jmc-instrument -mtriple=arm-pc-windows-msvc -S < %s | FileCheck %s + +; CHECK: $__JustMyCode_Default = comdat any + +; CHECK: @"__7DF23CF5_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 +; CHECK: @"__A85D9D03_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !5 +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @__JustMyCode_Default to i8*)], section "llvm.metadata" + +; CHECK: define void @l1() !dbg !13 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__7DF23CF5_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @l2() !dbg !17 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__7DF23CF5_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w1() !dbg !19 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w2() !dbg !20 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w3() !dbg !22 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w4() !dbg !24 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: declare void @__CheckForDebuggerJustMyCode(i8* noundef) unnamed_addr + +; CHECK: define void @__JustMyCode_Default(i8* noundef %0) unnamed_addr comdat { +; CHECK: ret void +; CHECK: } + +; CHECK: !llvm.linker.options = !{!12} + +; CHECK: !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +; CHECK: !1 = distinct !DIGlobalVariable(name: "__7DF23CF5_x@c", scope: !2, file: !3, type: !8, isLocal: true, isDefinition: true) +; CHECK: !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +; CHECK: !3 = !DIFile(filename: "a/x.c", directory: "/tmp") +; CHECK: !4 = !{!0, !5} +; CHECK: !5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) +; CHECK: !6 = distinct !DIGlobalVariable(name: "__A85D9D03_x@c", scope: !2, file: !7, type: !8, isLocal: true, isDefinition: true) +; CHECK: !7 = !DIFile(filename: "./x.c", directory: "C:\\\\a\\\\b\\\\") +; CHECK: !8 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char, flags: DIFlagArtificial) +; CHECK: !9 = !{i32 2, !"CodeView", i32 1} +; CHECK: !10 = !{i32 2, !"Debug Info Version", i32 3} +; CHECK: !11 = !{!"clang"} +; CHECK: !12 = !{!"/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default"} +; CHECK: !13 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +; CHECK: !14 = !DISubroutineType(types: !15) +; CHECK: !15 = !{null} +; CHECK: !16 = !{} +; CHECK: !17 = distinct !DISubprogram(name: "f", scope: !18, file: !18, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +; CHECK: !18 = !DIFile(filename: "x.c", directory: "/tmp/a") +; CHECK: !19 = distinct !DISubprogram(name: "f", scope: !7, file: !7, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +; CHECK: !20 = distinct !DISubprogram(name: "f", scope: !21, file: !21, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +; CHECK: !21 = !DIFile(filename: "./b\\x.c", directory: "C:\\\\a\\\\") +; CHECK: !22 = distinct !DISubprogram(name: "f", scope: !23, file: !23, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +; CHECK: !23 = !DIFile(filename: "./b/x.c", directory: "C:\\\\a\\\\") +; CHECK: !24 = distinct !DISubprogram(name: "f", scope: !25, file: !25, line: 1, type: !14, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !16) +; CHECK: !25 = !DIFile(filename: "./b/./../b/x.c", directory: "C:\\\\a") + +; All use the same flag +define void @l1() !dbg !10 { + ret void +} +define void @l2() !dbg !11 { + ret void +} + +; All use the same flag +define void @w1() !dbg !12 { + ret void +} +define void @w2() !dbg !13 { + ret void +} +define void @w3() !dbg !14 { + ret void +} +define void @w4() !dbg !15 { + ret void +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "a/x.c", directory: "/tmp") +!2 = !DIFile(filename: "x.c", directory: "/tmp/a") +!3 = !DIFile(filename: "./x.c", directory: "C:\\\\a\\\\b\\\\") +!4 = !DIFile(filename: "./b\\x.c", directory: "C:\\\\a\\\\") +!5 = !DIFile(filename: "./b/x.c", directory: "C:\\\\a\\\\") +!6 = !DIFile(filename: "./b/./../b/x.c", directory: "C:\\\\a") +!7 = !{i32 2, !"CodeView", i32 1} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{!"clang"} +!10 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!11 = distinct !DISubprogram(name: "f", scope: !2, file: !2, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!12 = distinct !DISubprogram(name: "f", scope: !3, file: !3, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!13 = distinct !DISubprogram(name: "f", scope: !4, file: !4, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!14 = distinct !DISubprogram(name: "f", scope: !5, file: !5, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!15 = distinct !DISubprogram(name: "f", scope: !6, file: !6, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!31 = !DISubroutineType(types: !32) +!32 = !{null} +!33 = !{} Index: llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll =================================================================== --- /dev/null +++ llvm/test/Instrumentation/JustMyCode/jmc-instrument-x86.ll @@ -0,0 +1,53 @@ +; RUN: opt -jmc-instrument -S < %s | FileCheck %s + +; CHECK: $_JustMyCode_Default = comdat any + +; CHECK: @"_A85D9D03_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 +; CHECK: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @_JustMyCode_Default to i8*)], section "llvm.metadata" + +; CHECK: define void @w1() #0 !dbg !10 { +; CHECK: call x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef @"_A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: declare x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef) unnamed_addr + +; CHECK: define void @_JustMyCode_Default(i8* inreg noundef %0) unnamed_addr comdat { +; CHECK: ret void +; CHECK: } + +; CHECK: !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +; CHECK: !1 = distinct !DIGlobalVariable(name: "_A85D9D03_x@c", scope: !2, file: !3, type: !5, isLocal: true, isDefinition: true) +; CHECK: !2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +; CHECK: !3 = !DIFile(filename: "./b/./../b/x.c", directory: "C:\\\\a\\\\") +; CHECK: !4 = !{!0} +; CHECK: !5 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char, flags: DIFlagArtificial) +; CHECK: !6 = !{i32 2, !"CodeView", i32 1} +; CHECK: !7 = !{i32 2, !"Debug Info Version", i32 3} +; CHECK: !8 = !{!"clang"} +; CHECK: !9 = !{!"/alternatename:__CheckForDebuggerJustMyCode=_JustMyCode_Default"} +; CHECK: !10 = distinct !DISubprogram(name: "w1", scope: !3, file: !3, line: 1, type: !11, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !13) +; CHECK: !11 = !DISubroutineType(types: !12) + +target datalayout = "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32-a:0:32-S32" +target triple = "i386-pc-windows-msvc" + +define void @w1() #0 !dbg !10 { + ret void +} + +attributes #0 = { "target-cpu"="pentium4" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "./b/./../b/x.c", directory: "C:\\\\a\\\\") +!7 = !{i32 2, !"CodeView", i32 1} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{!"clang"} +!10 = distinct !DISubprogram(name: "w1", scope: !1, file: !1, line: 1, type: !31, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !33) +!31 = !DISubroutineType(types: !32) +!32 = !{null} +!33 = !{} Index: llvm/lib/Target/X86/X86TargetMachine.cpp =================================================================== --- llvm/lib/Target/X86/X86TargetMachine.cpp +++ llvm/lib/Target/X86/X86TargetMachine.cpp @@ -441,6 +441,9 @@ addPass(createCFGuardCheckPass()); } } + + if (TM->Options.JMCInstrument) + addPass(createJMCInstrumenterPass()); } bool X86PassConfig::addInstSelector() { Index: llvm/lib/Target/ARM/ARMTargetMachine.cpp =================================================================== --- llvm/lib/Target/ARM/ARMTargetMachine.cpp +++ llvm/lib/Target/ARM/ARMTargetMachine.cpp @@ -434,6 +434,9 @@ // Add Control Flow Guard checks. if (TM->getTargetTriple().isOSWindows()) addPass(createCFGuardCheckPass()); + + if (TM->Options.JMCInstrument) + addPass(createJMCInstrumenterPass()); } void ARMPassConfig::addCodeGenPrepare() { Index: llvm/lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -574,6 +574,9 @@ // Add Control Flow Guard checks. if (TM->getTargetTriple().isOSWindows()) addPass(createCFGuardCheckPass()); + + if (TM->Options.JMCInstrument) + addPass(createJMCInstrumenterPass()); } // Pass Pipeline Configuration Index: llvm/lib/CodeGen/JMCInstrumenter.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/JMCInstrumenter.cpp @@ -0,0 +1,207 @@ +//===- JMCInstrumenter.cpp - JMC Instrumentation --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// JMCInstrumenter pass: +// - add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to +// "llvm.linker.options" +// - create the dummy COMDAT function __JustMyCode_Default +// - instrument each function with a call to __CheckForDebuggerJustMyCode. The +// sole argument should be defined in .msvcjmc. Each flag is 1 byte initilized +// to 1. +// - (TODO) currently targeting MSVC, adds ELF debuggers support +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/Path.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +using namespace llvm; + +#define DEBUG_TYPE "jmc-instrument" + +namespace { +struct JMCInstrumenter : public ModulePass { + static char ID; + JMCInstrumenter() : ModulePass(ID) { + initializeJMCInstrumenterPass(*PassRegistry::getPassRegistry()); + } + bool runOnModule(Module &M) override; +}; +char JMCInstrumenter::ID = 0; +} // namespace + +INITIALIZE_PASS( + JMCInstrumenter, DEBUG_TYPE, + "Instrument function entry with call to __CheckForDebuggerJustMyCode", + false, false) + +ModulePass *llvm::createJMCInstrumenterPass() { return new JMCInstrumenter(); } + +namespace { +const char CheckFunctionName[] = "__CheckForDebuggerJustMyCode"; + +std::string getFlagName(DISubprogram &SP, bool UseX86FastCall) { + // Best effort path normalization. This is to guarantee an unique flag symbol + // is produced for the same directory. Some builds may want to use relative + // paths, or paths with a specific prefix (see the -fdebug-compilation-dir + // flag), so only hash paths in debuginfo. Don't expand them to absolute + // paths. + SmallString<256> FilePath(SP.getDirectory()); + sys::path::append(FilePath, SP.getFilename()); + sys::path::native(FilePath); + sys::path::remove_dots(FilePath, /*remove_dot_dot=*/true); + + // The naming convention for the flag name is __<hash>_<file name> with '.' in + // <file name> replaced with '@'. For example C:\file.any.c would have a flag + // __D032E919_file@any@c. The naming convention match MSVC's format however + // the match is not required to make JMC work. The hashing function used here + // is different from MSVC's. + + std::string Suffix; + for (auto C : sys::path::filename(FilePath)) + Suffix.push_back(C == '.' ? '@' : C); + + sys::path::remove_filename(FilePath); + return (UseX86FastCall ? "_" : "__") + + utohexstr(djbHash(FilePath), /*LowerCase=*/false, + /*Width=*/8) + + "_" + Suffix; +} + +void attachDebugInfo(GlobalVariable &GV, DISubprogram &SP) { + Module &M = *GV.getParent(); + DICompileUnit *CU = SP.getUnit(); + assert(CU); + DIBuilder DB(M, false, CU); + + auto *DType = + DB.createBasicType("unsigned char", 8, dwarf::DW_ATE_unsigned_char, + llvm::DINode::FlagArtificial); + + auto *DGVE = DB.createGlobalVariableExpression( + CU, GV.getName(), /*LinkageName=*/StringRef(), SP.getFile(), + /*LineNo=*/0, DType, /*IsLocalToUnit=*/true, /*IsDefined=*/true); + GV.addMetadata(LLVMContext::MD_dbg, *DGVE); + DB.finalize(); +} + +FunctionType *getCheckFunctionType(LLVMContext &Ctx) { + Type *VoidTy = Type::getVoidTy(Ctx); + PointerType *VoidPtrTy = Type::getInt8PtrTy(Ctx); + return FunctionType::get(VoidTy, VoidPtrTy, false); +} + +void createDefaultCheckFunction(Module &M, bool UseX86FastCall) { + LLVMContext &Ctx = M.getContext(); + const char *DefaultCheckFunctionName = + UseX86FastCall ? "_JustMyCode_Default" : "__JustMyCode_Default"; + // Create the function. + Function *DefaultCheckFunc = + Function::Create(getCheckFunctionType(Ctx), GlobalValue::ExternalLinkage, + DefaultCheckFunctionName, &M); + DefaultCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + DefaultCheckFunc->addParamAttr(0, Attribute::NoUndef); + if (UseX86FastCall) + DefaultCheckFunc->addParamAttr(0, Attribute::InReg); + appendToUsed(M, {DefaultCheckFunc}); + Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName); + C->setSelectionKind(Comdat::Any); + DefaultCheckFunc->setComdat(C); + BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc); + ReturnInst::Create(Ctx, EntryBB); + + // Add a linker option /alternatename to set the default implementation for + // the check function. + // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024 + std::string AltOption = std::string("/alternatename:") + CheckFunctionName + + "=" + DefaultCheckFunctionName; + llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)}; + MDTuple *N = MDNode::get(Ctx, Ops); + M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N); +} +} // namespace + +bool JMCInstrumenter::runOnModule(Module &M) { + bool Changed = false; + LLVMContext &Ctx = M.getContext(); + Triple ModuleTriple(M.getTargetTriple()); + bool UseX86FastCall = + ModuleTriple.isOSWindows() && ModuleTriple.getArch() == Triple::x86; + + Function *CheckFunction = nullptr; + DenseMap<DISubprogram *, Constant *> SavedFlags(8); + for (auto &F : M) { + if (F.isDeclaration()) + continue; + auto *SP = F.getSubprogram(); + if (!SP) + continue; + + Constant *&Flag = SavedFlags[SP]; + if (!Flag) { + std::string FlagName = getFlagName(*SP, UseX86FastCall); + IntegerType *FlagTy = Type::getInt8Ty(Ctx); + Flag = M.getOrInsertGlobal(FlagName, FlagTy, [&] { + // FIXME: Put the GV in comdat and have linkonce_odr linkage to save + // .msvcjmc section space? maybe not worth it. + GlobalVariable *GV = new GlobalVariable( + M, FlagTy, /*isConstant=*/false, GlobalValue::InternalLinkage, + ConstantInt::get(FlagTy, 1), FlagName); + GV->setSection(".msvcjmc"); + GV->setAlignment(Align(1)); + GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + attachDebugInfo(*GV, *SP); + return GV; + }); + } + + if (!CheckFunction) { + assert(!M.getFunction(CheckFunctionName) && + "JMC instrument more than once?"); + CheckFunction = cast<Function>( + M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx)) + .getCallee()); + CheckFunction->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + CheckFunction->addParamAttr(0, Attribute::NoUndef); + if (UseX86FastCall) { + CheckFunction->setCallingConv(CallingConv::X86_FastCall); + CheckFunction->addParamAttr(0, Attribute::InReg); + } + } + // FIXME: it would be nice to make CI scheduling boundary, although in + // practice it does not matter much. + auto *CI = CallInst::Create(CheckFunction, {Flag}, "", + &*F.begin()->getFirstInsertionPt()); + CI->addParamAttr(0, Attribute::NoUndef); + if (UseX86FastCall) { + CI->setCallingConv(CallingConv::X86_FastCall); + CI->addParamAttr(0, Attribute::InReg); + } + + Changed = true; + } + if (!Changed) + return false; + + createDefaultCheckFunction(M, UseX86FastCall); + return true; +} Index: llvm/lib/CodeGen/CommandFlags.cpp =================================================================== --- llvm/lib/CodeGen/CommandFlags.cpp +++ llvm/lib/CodeGen/CommandFlags.cpp @@ -94,6 +94,7 @@ CGOPT(bool, XRayOmitFunctionIndex) CGOPT(bool, DebugStrictDwarf) CGOPT(unsigned, AlignLoops) +CGOPT(bool, JMCInstrument) codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() { #define CGBINDOPT(NAME) \ @@ -457,6 +458,12 @@ cl::desc("Default alignment for loops")); CGBINDOPT(AlignLoops); + static cl::opt<bool> JMCInstrument( + "enable-jmc-instrument", + cl::desc("Instrument functions with a call to __CheckForDebuggerJustMyCode"), + cl::init(false)); + CGBINDOPT(JMCInstrument); + #undef CGBINDOPT mc::RegisterMCTargetOptionsFlags(); @@ -531,6 +538,7 @@ Options.XRayOmitFunctionIndex = getXRayOmitFunctionIndex(); Options.DebugStrictDwarf = getDebugStrictDwarf(); Options.LoopAlignment = getAlignLoops(); + Options.JMCInstrument = getJMCInstrument(); Options.MCOptions = mc::InitMCTargetOptionsFromFlags(); Index: llvm/lib/CodeGen/CodeGen.cpp =================================================================== --- llvm/lib/CodeGen/CodeGen.cpp +++ llvm/lib/CodeGen/CodeGen.cpp @@ -50,6 +50,7 @@ initializeIndirectBrExpandPassPass(Registry); initializeInterleavedLoadCombinePass(Registry); initializeInterleavedAccessPass(Registry); + initializeJMCInstrumenterPass(Registry); initializeLiveDebugValuesPass(Registry); initializeLiveDebugVariablesPass(Registry); initializeLiveIntervalsPass(Registry); Index: llvm/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/CMakeLists.txt +++ llvm/lib/CodeGen/CMakeLists.txt @@ -75,6 +75,7 @@ InterleavedAccessPass.cpp InterleavedLoadCombinePass.cpp IntrinsicLowering.cpp + JMCInstrumenter.cpp LatencyPriorityQueue.cpp LazyMachineBlockFrequencyInfo.cpp LexicalScopes.cpp Index: llvm/include/llvm/Target/TargetOptions.h =================================================================== --- llvm/include/llvm/Target/TargetOptions.h +++ llvm/include/llvm/Target/TargetOptions.h @@ -142,7 +142,7 @@ SupportsDebugEntryValues(false), EnableDebugEntryValues(false), ValueTrackingVariableLocations(false), ForceDwarfFrameSection(false), XRayOmitFunctionIndex(false), DebugStrictDwarf(false), - Hotpatch(false), + Hotpatch(false), JMCInstrument(false), FPDenormalMode(DenormalMode::IEEE, DenormalMode::IEEE) {} /// DisableFramePointerElim - This returns true if frame pointer elimination @@ -345,6 +345,9 @@ /// Emit the hotpatch flag in CodeView debug. unsigned Hotpatch : 1; + /// Enable JustMyCode instrumentation. + unsigned JMCInstrument : 1; + /// Name of the stack usage file (i.e., .su file) if user passes /// -fstack-usage. If empty, it can be implied that -fstack-usage is not /// passed on the command line. Index: llvm/include/llvm/LinkAllPasses.h =================================================================== --- llvm/include/llvm/LinkAllPasses.h +++ llvm/include/llvm/LinkAllPasses.h @@ -123,6 +123,7 @@ (void) llvm::createInstSimplifyLegacyPass(); (void) llvm::createInstructionCombiningPass(); (void) llvm::createInternalizePass(); + (void) llvm::createJMCInstrumenterPass(); (void) llvm::createLCSSAPass(); (void) llvm::createLegacyDivergenceAnalysisPass(); (void) llvm::createLICMPass(); Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -215,6 +215,7 @@ void initializeInterleavedLoadCombinePass(PassRegistry &); void initializeInternalizeLegacyPassPass(PassRegistry&); void initializeIntervalPartitionPass(PassRegistry&); +void initializeJMCInstrumenterPass(PassRegistry&); void initializeJumpThreadingPass(PassRegistry&); void initializeLCSSAVerificationPassPass(PassRegistry&); void initializeLCSSAWrapperPassPass(PassRegistry&); Index: llvm/include/llvm/CodeGen/Passes.h =================================================================== --- llvm/include/llvm/CodeGen/Passes.h +++ llvm/include/llvm/CodeGen/Passes.h @@ -554,6 +554,9 @@ /// When learning an eviction policy, extract score(reward) information, /// otherwise this does nothing FunctionPass *createRegAllocScoringPass(); + + /// JMC instrument pass. + ModulePass *createJMCInstrumenterPass(); } // End llvm namespace #endif Index: llvm/include/llvm/CodeGen/CommandFlags.h =================================================================== --- llvm/include/llvm/CodeGen/CommandFlags.h +++ llvm/include/llvm/CodeGen/CommandFlags.h @@ -140,6 +140,8 @@ unsigned getAlignLoops(); +bool getJMCInstrument(); + /// Create this object with static storage to register codegen-related command /// line options. struct RegisterCodeGenFlags { Index: clang/test/Driver/cl-options.c =================================================================== --- clang/test/Driver/cl-options.c +++ clang/test/Driver/cl-options.c @@ -486,7 +486,6 @@ // RUN: /GZ \ // RUN: /H \ // RUN: /homeparams \ -// RUN: /JMC \ // RUN: /kernel \ // RUN: /LN \ // RUN: /MP \ @@ -772,4 +771,14 @@ // FAKEDIR: "-libpath:/foo{{/|\\\\}}Lib{{/|\\\\}}10.0.12345.0{{/|\\\\}}ucrt // FAKEDIR: "-libpath:/foo{{/|\\\\}}Lib{{/|\\\\}}10.0.12345.0{{/|\\\\}}um +// RUN: %clang_cl /JMC /c -### -- %s 2>&1 | FileCheck %s --check-prefix JMCWARN +// JMCWARN: /JMC requires debug info. Use '/Zi', '/Z7' or other debug options; option ignored + +// RUN: %clang_cl /JMC /c -### -- %s 2>&1 | FileCheck %s --check-prefix NOJMC +// RUN: %clang_cl /JMC /Z7 /JMC- /c -### -- %s 2>&1 | FileCheck %s --check-prefix NOJMC +// NOJMC-NOT: -fjmc + +// RUN: %clang_cl /JMC /Z7 /c -### -- %s 2>&1 | FileCheck %s --check-prefix JMC +// JMC: -fjmc + void f() { } Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -7451,6 +7451,16 @@ } const Driver &D = getToolChain().getDriver(); + + // This controls whether or not we perform JustMyCode instrumentation. + if (Args.hasFlag(options::OPT__SLASH_JMC, options::OPT__SLASH_JMC_, + /*Default=*/false)) { + if (*EmitCodeView && *DebugInfoKind >= codegenoptions::DebugInfoConstructor) + CmdArgs.push_back("-fjmc"); + else + D.Diag(clang::diag::warn_drv_jmc_requires_debuginfo); + } + EHFlags EH = parseClangCLEHFlags(D, Args); if (!isNVPTX && (EH.Synch || EH.Asynch)) { if (types::isCXX(InputType)) Index: clang/lib/CodeGen/BackendUtil.cpp =================================================================== --- clang/lib/CodeGen/BackendUtil.cpp +++ clang/lib/CodeGen/BackendUtil.cpp @@ -605,6 +605,10 @@ Options.EnableAIXExtendedAltivecABI = CodeGenOpts.EnableAIXExtendedAltivecABI; Options.XRayOmitFunctionIndex = CodeGenOpts.XRayOmitFunctionIndex; Options.LoopAlignment = CodeGenOpts.LoopAlignment; + Options.DebugStrictDwarf = CodeGenOpts.DebugStrictDwarf; + Options.ObjectFilenameForDebug = CodeGenOpts.ObjectFilenameForDebug; + Options.Hotpatch = CodeGenOpts.HotPatch; + Options.JMCInstrument = CodeGenOpts.JMCInstrument; switch (CodeGenOpts.getSwiftAsyncFramePointer()) { case CodeGenOptions::SwiftAsyncFramePointerKind::Auto: @@ -643,9 +647,6 @@ Entry.IgnoreSysRoot ? Entry.Path : HSOpts.Sysroot + Entry.Path); Options.MCOptions.Argv0 = CodeGenOpts.Argv0; Options.MCOptions.CommandLineArgs = CodeGenOpts.CommandLineArgs; - Options.DebugStrictDwarf = CodeGenOpts.DebugStrictDwarf; - Options.ObjectFilenameForDebug = CodeGenOpts.ObjectFilenameForDebug; - Options.Hotpatch = CodeGenOpts.HotPatch; return true; } Index: clang/include/clang/Driver/Options.td =================================================================== --- clang/include/clang/Driver/Options.td +++ clang/include/clang/Driver/Options.td @@ -1441,6 +1441,9 @@ HelpText<"Do not elide types when printing diagnostics">, MarshallingInfoNegativeFlag<DiagnosticOpts<"ElideType">>; def feliminate_unused_debug_symbols : Flag<["-"], "feliminate-unused-debug-symbols">, Group<f_Group>; +def fjmc : Flag<["-"], "fjmc">, Group<f_Group>,Flags<[CC1Option]>, + HelpText<"Enable just-my-code debugging">, + MarshallingInfoFlag<CodeGenOpts<"JMCInstrument">>; defm eliminate_unused_debug_types : OptOutCC1FFlag<"eliminate-unused-debug-types", "Do not emit ", "Emit ", " debug info for defined but unused types">; def femit_all_decls : Flag<["-"], "femit-all-decls">, Group<f_Group>, Flags<[CC1Option]>, @@ -6324,6 +6327,10 @@ def _SLASH_imsvc : CLJoinedOrSeparate<"imsvc">, HelpText<"Add <dir> to system include search path, as if in %INCLUDE%">, MetaVarName<"<dir>">; +def _SLASH_JMC : CLFlag<"JMC">, + HelpText<"Enable just-my-code debugging">; +def _SLASH_JMC_ : CLFlag<"JMC-">, + HelpText<"Disable just-my-code debugging (default)">; def _SLASH_LD : CLFlag<"LD">, HelpText<"Create DLL">; def _SLASH_LDd : CLFlag<"LDd">, HelpText<"Create debug DLL">; def _SLASH_link : CLRemainingArgsJoined<"link">, @@ -6424,7 +6431,6 @@ def _SLASH_FC : CLIgnoredFlag<"FC">; def _SLASH_Fd : CLIgnoredJoined<"Fd">; def _SLASH_FS : CLIgnoredFlag<"FS">; -def _SLASH_JMC : CLIgnoredFlag<"JMC">; def _SLASH_kernel_ : CLIgnoredFlag<"kernel-">; def _SLASH_nologo : CLIgnoredFlag<"nologo">; def _SLASH_RTC : CLIgnoredJoined<"RTC">; Index: clang/include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticDriverKinds.td +++ clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -631,4 +631,8 @@ "Only one offload target is supported in %0.">; def err_drv_invalid_or_unsupported_offload_target : Error< "Invalid or unsupported offload target: '%0'.">; + +def warn_drv_jmc_requires_debuginfo : Warning< + "/JMC requires debug info. Use '/Zi', '/Z7' or other debug options; option ignored">, + InGroup<OptionIgnored>; } Index: clang/include/clang/Basic/CodeGenOptions.def =================================================================== --- clang/include/clang/Basic/CodeGenOptions.def +++ clang/include/clang/Basic/CodeGenOptions.def @@ -144,6 +144,7 @@ CODEGENOPT(HotPatch, 1, 0) ///< Supports the Microsoft /HOTPATCH flag and ///< generates a 'patchable-function' attribute. +CODEGENOPT(JMCInstrument, 1, 0) ///< Set when -fjmc is enabled. CODEGENOPT(InstrumentForProfiling , 1, 0) ///< Set when -pg is enabled. CODEGENOPT(CallFEntry , 1, 0) ///< Set when -mfentry is enabled. CODEGENOPT(MNopMCount , 1, 0) ///< Set when -mnop-mcount is enabled. Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -151,6 +151,12 @@ along with tools such as Live++ or Recode. Microsoft Edit and Continue isn't currently supported. +- Add support for MSVC-compatible ``/JMC``/``/JMC-`` flag in clang-cl (supports + X86/X64/ARM/ARM64). ``/JMC`` could only be used when ``/Zi`` or ``/Z7`` is + turned on. With this addition, clang-cl can be used in Visual Studio for the + JustMyCode feature. Note, you may need to manually add ``/JMC`` as additional + compile options in the Visual Studio since it currently assumes clang-cl does not support ``/JMC``. + C Language Changes in Clang ---------------------------
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits