ychen updated this revision to Diff 403870. ychen added a comment. - update
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/AsmPrinter/CodeViewDebug.cpp llvm/lib/CodeGen/CMakeLists.txt llvm/lib/CodeGen/CodeGen.cpp llvm/lib/CodeGen/CommandFlags.cpp llvm/lib/CodeGen/JMCInstrumenter.cpp llvm/lib/CodeGen/TargetPassConfig.cpp llvm/test/CodeGen/X86/jmc-instrument-32bit.ll llvm/test/CodeGen/X86/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/CodeGen/X86/jmc-instrument.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/jmc-instrument.ll @@ -0,0 +1,148 @@ +; RUN: opt -jmc-instrument -S < %s | FileCheck %s +; RUN: llc < %s -enable-jmc-instrument | FileCheck %s -check-prefix CODEGEN + +; 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 [3 x i8*] [i8* @"__7DF23CF5_x@c", i8* @"__A85D9D03_x@c", i8* bitcast (void (i8*)* @__JustMyCode_Default to i8*)], section "llvm.metadata" + +; CHECK: define void @l1() #0 !dbg !13 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__7DF23CF5_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @l2() #0 !dbg !17 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__7DF23CF5_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w1() #0 !dbg !19 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w2() #0 !dbg !20 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w3() #0 !dbg !22 { +; CHECK: call void @__CheckForDebuggerJustMyCode(i8* noundef @"__A85D9D03_x@c") +; CHECK: ret void +; CHECK: } + +; CHECK: define void @w4() #0 !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: attributes {{.*}} "jmc-instrumented" + +; 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") + +; CODEGEN: l1: +; CODEGEN: leaq __7DF23CF5_x@c(%rip), %rcx +; CODEGEN: callq __CheckForDebuggerJustMyCode +; CODEGEN: l2: +; CODEGEN: leaq __7DF23CF5_x@c(%rip), %rcx +; CODEGEN: callq __CheckForDebuggerJustMyCode +; CODEGEN: w1: +; CODEGEN: leaq __A85D9D03_x@c(%rip), %rcx +; CODEGEN: callq __CheckForDebuggerJustMyCode +; CODEGEN: w2: +; CODEGEN: leaq __A85D9D03_x@c(%rip), %rcx +; CODEGEN: callq __CheckForDebuggerJustMyCode +; CODEGEN: w3: +; CODEGEN: leaq __A85D9D03_x@c(%rip), %rcx +; CODEGEN: callq __CheckForDebuggerJustMyCode +; CODEGEN: w4: +; CODEGEN: leaq __A85D9D03_x@c(%rip), %rcx +; CODEGEN: callq __CheckForDebuggerJustMyCode +; CODEGEN: __JustMyCode_Default: +; CODEGEN: retq + + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +; All use the same flag +define void @l1() #0 !dbg !10 { + ret void +} +define void @l2() #0 !dbg !11 { + ret void +} + +; All use the same flag +define void @w1() #0 !dbg !12 { + ret void +} +define void @w2() #0 !dbg !13 { + ret void +} +define void @w3() #0 !dbg !14 { + ret void +} +define void @w4() #0 !dbg !15 { + ret void +} + +attributes #0 = { "target-cpu"="x86-64" "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: "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/CodeGen/X86/jmc-instrument-32bit.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/jmc-instrument-32bit.ll @@ -0,0 +1,64 @@ +; RUN: opt -jmc-instrument -S < %s | FileCheck %s +; RUN: llc < %s -enable-jmc-instrument | FileCheck %s -check-prefix CODEGEN + +; 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 [2 x i8*] [i8* @"_A85D9D03_x@c", 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: attributes {{.*}} "jmc-instrumented" + +; 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) + +; CODEGEN: _w1: +; CODEGEN: movl $__A85D9D03_x@c, %ecx +; CODEGEN: calll @__CheckForDebuggerJustMyCode@4 +; CODEGEN: __JustMyCode_Default: +; CODEGEN: retl + + +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" + +; All use the same flag +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/CodeGen/TargetPassConfig.cpp =================================================================== --- llvm/lib/CodeGen/TargetPassConfig.cpp +++ llvm/lib/CodeGen/TargetPassConfig.cpp @@ -1092,6 +1092,8 @@ addPass(createPreISelIntrinsicLoweringPass()); PM->add(createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); addIRPasses(); + if (TM->Options.JMCInstrument) + addPass(createJMCInstrumenterPass()); addCodeGenPrepare(); addPassesToHandleExceptions(); addISelPrepare(); Index: llvm/lib/CodeGen/JMCInstrumenter.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/JMCInstrumenter.cpp @@ -0,0 +1,209 @@ +//===- 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 +// - (TODO) calls to __CheckForDebuggerJustMyCode should be a scheduling +// boundary +// +//===----------------------------------------------------------------------===// + +#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/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 { + +std::string getFlagName(DISubprogram &SP, bool Is32Bit) { + // Best effort path normalization. This is to guarantee an unique flag symbol + // is produced for the same directory. Ideally maybe it's better to call + // `realpath` on Linux and `GetFullPathName()` on Windows + // (https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#path-normalization). + 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 hashing function or the naming convention match + // MSVC's behavior however the match is not required to make JMC work. + + std::string Suffix; + for (auto C : sys::path::filename(FilePath)) + Suffix.push_back(C == '.' ? '@' : C); + + sys::path::remove_filename(FilePath); + return (Is32Bit ? "_" : "__") + + 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, const char *CheckFunctionName, + bool Is32Bit) { + LLVMContext &Ctx = M.getContext(); + const char *DefaultCheckFunctionName = + Is32Bit ? "_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 (Is32Bit) + 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 options /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); + + // Micro-optimization: set the necessary function information here instead of + // when creating it. + Function *Decl = M.getFunction(CheckFunctionName); + assert(Decl); + Decl->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); + Decl->addParamAttr(0, Attribute::NoUndef); + if (Is32Bit) { + Decl->setCallingConv(CallingConv::X86_FastCall); + Decl->addParamAttr(0, Attribute::InReg); + } +} +} // namespace + +bool JMCInstrumenter::runOnModule(Module &M) { + bool Changed = false; + const char *EntryAttr = "jmc-instrumented"; + const char *CheckFunctionName = "__CheckForDebuggerJustMyCode"; + bool Is32Bit = Triple(M.getTargetTriple()).getArch() == Triple::x86; + + for (auto &F : M) { + if (F.isDeclaration()) + continue; + auto *SP = F.getSubprogram(); + if (!SP) + continue; + + // If the attribute is specified, insert instrumentation and then "consume" + // the attribute so that it's not inserted again if the pass should happen + // to run later for some reason. + if (F.hasFnAttribute(EntryAttr)) + continue; + + LLVMContext &Ctx = F.getContext(); + FunctionCallee Fn = + M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx)); + // FIXME: caching the flag name to prevent repetitive hashing? + std::string FlagName = getFlagName(*SP, Is32Bit); + IntegerType *FlagTy = Type::getInt8Ty(Ctx); + Constant *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. + // Make it static linkage. One of the definition would win, the rest are + // dead in .msvcjmc section, but it is supposed be very cheap. + 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); + appendToUsed(M, {GV}); + return GV; + }); + auto *CI = + CallInst::Create(Fn, {Flag}, "", &*F.begin()->getFirstInsertionPt()); + CI->addParamAttr(0, Attribute::NoUndef); + if (Is32Bit) { + CI->setCallingConv(CallingConv::X86_FastCall); + CI->addParamAttr(0, Attribute::InReg); + } + + F.addFnAttr(EntryAttr); + Changed = true; + } + if (!Changed) + return false; + + createDefaultCheckFunction(M, CheckFunctionName, Is32Bit); + 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, EnableJMCInstrument) codegen::RegisterCodeGenFlags::RegisterCodeGenFlags() { #define CGBINDOPT(NAME) \ @@ -457,6 +458,12 @@ cl::desc("Default alignment for loops")); CGBINDOPT(AlignLoops); + static cl::opt<bool> EnableJMCInstrument( + "enable-jmc-instrument", + cl::desc("Instrument functions with a call to __CheckForDebuggerJustMyCode"), + cl::init(false)); + CGBINDOPT(EnableJMCInstrument); + #undef CGBINDOPT mc::RegisterMCTargetOptionsFlags(); @@ -531,6 +538,7 @@ Options.XRayOmitFunctionIndex = getXRayOmitFunctionIndex(); Options.DebugStrictDwarf = getDebugStrictDwarf(); Options.LoopAlignment = getAlignLoops(); + Options.JMCInstrument = getEnableJMCInstrument(); 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/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -3355,14 +3355,18 @@ if (const auto *MemberDecl = dyn_cast_or_null<DIDerivedType>( DIGV->getRawStaticDataMemberDeclaration())) Scope = MemberDecl->getScope(); + + const GlobalVariable *GV = CVGV.GVInfo.dyn_cast<const GlobalVariable *>(); + // For Fortran, the scoping portion is elided in its name so that we can // reference the variable in the command line of the VS debugger. + // For JMC, the flag symbols do not need qualifiers. std::string QualifiedName = - (moduleIsInFortran()) ? std::string(DIGV->getName()) - : getFullyQualifiedName(Scope, DIGV->getName()); + (moduleIsInFortran() || (GV && GV->getSection() == ".msvcjmc")) + ? std::string(DIGV->getName()) + : getFullyQualifiedName(Scope, DIGV->getName()); - if (const GlobalVariable *GV = - CVGV.GVInfo.dyn_cast<const GlobalVariable *>()) { + if (GV) { // DataSym record, see SymbolRecord.h for more info. Thread local data // happens to have the same format as global data. MCSymbol *GVSym = Asm->getSymbol(GV); 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 getEnableJMCInstrument(); + /// 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,13 @@ // 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 '/Zi or /Z7'; option ignored + +// RUN: %clang_cl /JMC /c -### -- %s 2>&1 | FileCheck %s --check-prefix JMC +// JMC-NOT: -fjmc + +// RUN: %clang_cl /JMC /Z7 /c -### -- %s 2>&1 | FileCheck %s --check-prefix JMCZ7 +// JMCZ7: -fjmc + void f() { } Index: clang/lib/Driver/ToolChains/Clang.cpp =================================================================== --- clang/lib/Driver/ToolChains/Clang.cpp +++ clang/lib/Driver/ToolChains/Clang.cpp @@ -7434,19 +7434,32 @@ CmdArgs.push_back(Args.MakeArgString(Twine(LangOptions::SSPStrong))); } + const Driver &D = getToolChain().getDriver(); + // Emit CodeView if -Z7 or -gline-tables-only are present. + bool EnabledZ7 = false; if (Arg *DebugInfoArg = Args.getLastArg(options::OPT__SLASH_Z7, options::OPT_gline_tables_only)) { *EmitCodeView = true; - if (DebugInfoArg->getOption().matches(options::OPT__SLASH_Z7)) + if (DebugInfoArg->getOption().matches(options::OPT__SLASH_Z7)) { + EnabledZ7 = true; *DebugInfoKind = codegenoptions::DebugInfoConstructor; - else + } else { *DebugInfoKind = codegenoptions::DebugLineTablesOnly; + } } else { *EmitCodeView = false; } - 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 (EnabledZ7) + CmdArgs.push_back("-fjmc"); + else + D.Diag(clang::diag::warn_drv_jmc_requires_z7); + } + 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 native C/C++ 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 native C/C++ Just My Code debugging">; +def _SLASH_JMC_ : CLFlag<"JMC-">, + HelpText<"Disable native C/C++ 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_z7 : Warning< + "/JMC requires '/Zi or /Z7'; 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,13 @@ 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, and + equivalent -cc1 flag ``-fjmc``. ``/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