This is an archive of the discontinued LLVM Phabricator instance.

[clang-cl] Support the /JMC flag
ClosedPublic

Authored by ychen on Jan 27 2022, 7:59 PM.

Details

Summary

The introduction and some examples are on this page:
https://devblogs.microsoft.com/cppblog/announcing-jmc-stepping-in-visual-studio/

The /JMC flag enables these instrumentations:

  • Insert at the beginning of every function immediately after the prologue with a call to void __fastcall __CheckForDebuggerJustMyCode(unsigned char *JMC_flag). The argument for __CheckForDebuggerJustMyCode is the address of a boolean global variable (the global variable is initialized to 1) with the name convention __<hash>_<filename>. All such global variables are placed in the .msvcjmc section.
  • The <hash> part of __<hash>_<filename> has a one-to-one mapping with a directory path. MSVC uses some unknown hashing function. Here I used DJB.
  • Add a dummy/empty COMDAT function __JustMyCode_Default.
  • Add /alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default link option via ".drectve" section. This is to prevent failure in case __CheckForDebuggerJustMyCode is not provided during linking.

Implementation:
All the instrumentations are implemented in an IR codegen pass. The pass is placed immediately before CodeGenPrepare pass. This is to not interfere with mid-end optimizations and make the instrumentation target-independent (I'm still working on an ELF port in a separate patch).

Diff Detail

Event Timeline

ychen created this revision.Jan 27 2022, 7:59 PM
ychen requested review of this revision.Jan 27 2022, 7:59 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptJan 27 2022, 7:59 PM
ychen edited the summary of this revision. (Show Details)Jan 27 2022, 7:59 PM
ychen edited the summary of this revision. (Show Details)
ychen edited the summary of this revision. (Show Details)
ychen updated this revision to Diff 403870.Jan 27 2022, 8:41 PM
  • update
aganea added a subscriber: belkiss.

Cool! :) Seems good generally.
Sounds like an expensive flag for the runtime perf :-D But I guess it makes the debugging experience a bit nicer.

clang/lib/Driver/ToolChains/Clang.cpp
7494

Can we do here if (*EmitCodeView && *DebugInfoKind >= codegenoptions::DebugInfoConstructor)? Is it worth introducing a new variable?

clang/test/Driver/cl-options.c
782

Perhaps add a test %clang_cl /JMC /JMC-

llvm/lib/CodeGen/JMCInstrumenter.cpp
132

s/options/option/

ychen marked 2 inline comments as done.Jan 29 2022, 3:33 PM

Thanks a lot for reviewing the patch!

Cool! :) Seems good generally.
Sounds like an expensive flag for the runtime perf :-D But I guess it makes the debugging experience a bit nicer.

Exactly. I had the same thoughts. For MSVC it is what it is. For ELF, the other choice from using the same scheme as MSVC is that we could inline the __CheckForDebuggerJustMyCode call (its definition is here: https://github.com/ojdkbuild/tools_toolchain_vs2017bt_1416/blob/master/VC/Tools/MSVC/14.16.27023/crt/src/vcruntime/debugger_jmc.c) before the function ABI prologue. For example, to instrument a function _foo:

  andb r11l, _jmc_global_flag, _jmc_enable_per_thread_step
  andb r11l, r11, _jmc_per_module_flag
  andb r11l, r11, _jmc_per_file_flag
  cmpb r11l, 0
  jne _foo 
  int 3
_foo:

The parameter of __CheckForDebuggerJustMyCode is _jmc_per_file_flag above. int 3 is basically a per-function flag. _jmc_per_module_flag and _jmc_global_flag control the JMC behavior at module-level or program-level for better performance. Otherwise, the debugger needs the gather this information using debuginfo. This scheme is target-dependent and increases the .text slightly. But it eliminates the call and the flag modification at different granularity is fast. With that being said, experts working on ELF debuggers have ideas about which scheme is better. I'll send an RFC for ELF after this patch goes in.

clang/lib/Driver/ToolChains/Clang.cpp
7494

Yeah, I think that works better. No need for a new variable.

ychen updated this revision to Diff 404307.Jan 29 2022, 3:34 PM
  • address Alexandre's comments
hans added a comment.Jan 31 2022, 5:45 AM

This is great stuff, thanks!

clang/docs/ReleaseNotes.rst
84

The cc1 flag is more of an implementation detail, so not sure it's worth mentioning in the release notes. Or do we want "clang -fjmc" to work?

clang/include/clang/Driver/Options.td
6370

I'm not sure "native C/C++" adds much info in the context of clang. Maybe just "Enable Just My Code debugging" is enough?

(Also, how do we want to spell this? Other features are not typically capitalized in clang.. so maybe "enable just my code debugging" or "enable just-my-code debugging"?)

clang/lib/Driver/ToolChains/Clang.cpp
7496

Why that level specifically and not e.g. *DebugInfoKind > NoDebugInfo?

clang/test/Driver/cl-options.c
775
  • is missing from this run line?
779

It seemed odd to me that the JMC check prefix is used for tests which check that JMC is *not* enabled.

How about using "NOJMC" for these, and "JMC" for the test below where it's enabled?

llvm/lib/CodeGen/CommandFlags.cpp
469

Other "on/off" options don't seem to have "enable" in the name or flag spelling, e.g. "-strict-dwarf", not "-enable-strict-dwarf". Maybe this should be just "-jmc-instrument" and JMCInstrument?

llvm/lib/CodeGen/JMCInstrumenter.cpp
19

Is that necessary? I assume nothing interesting would get scheduled across such a call anyway?

67

I don't think we'd want to call realpath or GetFullPathName() as some builds may want to use relative paths, or paths with a specific prefix (see the -fdebug-compilation-dir flag)

76

Should probably point out here (or below) that we're not using the same hash as MSVC.

112

What's the purpose of the default check function? Doesn't the CRT always provide one? (And if it doesn't, will JMC work at all?)

144

What if there are no function definitions in the module? In that case, I think we would not have created the function declaration, and this assert would fail?

I think instead of this micro-optimization, it would be safer set the function attributes etc. when creating the decl, and cache it. It could be a lambda in runOnModule() or maybe a helper method in the class with a member variable to hold the cached decl?

Or it could even just be declared outside the main loop in runOnModule() and in the loop you could do

if (!CheckFunction) {
  CheckFunction = ...
  add attributes and stuff
}
157

if this and EntryAttr can't change, maybe they should be const, and maybe declared outside the function?

158

That's a very x86 specific check. What about other targets? (Also the calling conventions are x86 specific..)

169

Do other instrumentation passes consume the attr like this? Why would the pass run again?

Actually it looks like the code does the opposite of consuming: it looks for the attribute, and *adds* it if it's not there..

175

As mentioned above, the decl could be cached instead, there's no need to look it up each time.

176

Hashing is cheap, but since we're likely to use the same filename again and again, and it does take some string manipulation, maybe caching the result is a good idea. Actually, could we cache the flag Constant rather than the name?

183

I thought static linkage meant they all go into the binary.. how do you mean one of them wins and the others are dead?

191

is the appendToUsed necessary? It will be used by the call instruction (and if that goes away for some reason, I guess we'd want the variable to go away too?)

llvm/test/CodeGen/X86/jmc-instrument-32bit.ll
2 ↗(On Diff #404307)

Mixing opt and llc testing like this seems unusual. The pass is really an instrumentation pass, so I suppose llvm/test/Instrumentation/ would be a better place, and I don't think the llc test is needed?

aganea added inline comments.Jan 31 2022, 6:15 AM
clang/docs/ReleaseNotes.rst
84

@ychen @hans An additional question is whether we want to use the same cc1 flag for an potential ELF port?

clang/lib/Driver/ToolChains/Clang.cpp
7496

@hans MSVC does this check too, not sure why.

D:\git\llvm-project>cl main.cpp /c /JMC
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30139 for ARM64
Copyright (C) Microsoft Corporation.  All rights reserved.

cl : Command line warning D9007 : '/JMC' requires '/Zi, /ZI or /Z7'; option ignored
main.cpp
llvm/lib/CodeGen/JMCInstrumenter.cpp
112

@hans That's what MSVC does, to avoid a linker error if the CRT isn't linked in:

D:\git\llvm-project>cat main.cpp
int main() { }
D:\git\llvm-project>cl main.cpp /JMC /Z7 /c
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30139 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp

D:\git\llvm-project>dumpbin /disasm main.obj
Microsoft (R) COFF/PE Dumper Version 14.29.30139.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file main.obj

File Type: COFF OBJECT

main:
  0000000000000000: 48 83 EC 28        sub         rsp,28h
  0000000000000004: 48 8D 0D 00 00 00  lea         rcx,[__BB04C4AD_main@cpp]
                    00
  000000000000000B: E8 00 00 00 00     call        __CheckForDebuggerJustMyCode
  0000000000000010: 33 C0              xor         eax,eax
  0000000000000012: 48 83 C4 28        add         rsp,28h
  0000000000000016: C3                 ret

__JustMyCode_Default:
  0000000000000000: C2 00 00           ret         0

  Summary

          50 .chks64
         228 .debug$S
         3EC .debug$T
          70 .drectve
           1 .msvcjmc
           C .pdata
          1A .text$mn
           8 .xdata

D:\git\llvm-project>dumpbin /all main.obj | grep -B12 -A5 'COMDAT; sym= __JustMyCode_Default'
SECTION HEADER #5
.text$mn name
       0 physical address
       0 virtual address
       3 size of raw data
     438 file pointer to raw data (00000438 to 0000043A)
       0 file pointer to relocation table
       0 file pointer to line numbers
       0 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= __JustMyCode_Default
         16 byte align
         Execute Read

RAW DATA #5
  00000000: C2 00 00                                         □..
158

@ychen Worth nothing that /JMC works on ARM/ARM64 as well. Should we support that too, not sure that's what @hans is implying?

D:\git\llvm-project>cl main.cpp /c /JMC /Z7
Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30139 for ARM64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp

D:\git\llvm-project>dumpbin /disasm main.obj
Microsoft (R) COFF/PE Dumper Version 14.29.30139.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file main.obj

File Type: COFF OBJECT

main:
  0000000000000000: A9BF7BFD  stp         fp,lr,[sp,#-0x10]!
  0000000000000004: 910003FD  mov         fp,sp
  0000000000000008: 90000008  adrp        x8,__BB04C4AD_main@cpp
  000000000000000C: 91000100  add         x0,x8,__BB04C4AD_main@cpp
  0000000000000010: 94000000  bl          __CheckForDebuggerJustMyCode
  0000000000000014: 52800000  mov         w0,#0
  0000000000000018: A8C17BFD  ldp         fp,lr,[sp],#0x10
  000000000000001C: D65F03C0  ret

__JustMyCode_Default:
  0000000000000000: D65F03C0  ret

  Summary

          48 .chks64
         228 .debug$S
         3F0 .debug$T
          70 .drectve
           1 .msvcjmc
           8 .pdata
          24 .text$mn
ychen updated this revision to Diff 404805.Jan 31 2022, 9:15 PM
ychen marked 10 inline comments as done.
  • address hans's comments
ychen added a comment.Jan 31 2022, 9:18 PM

@hans @aganea I think all comments are addressed except that I don't know how to test the ARM/ARM64 JMC function (I don't have ARM hardware or is there any ARM-based Windows virtual machine?). PTAL.

clang/docs/ReleaseNotes.rst
84

Removed. Yeah, I think "clang -fjmc" for an ELF implementation makes sense.

clang/include/clang/Driver/Options.td
6370

Used "enable just-my-code debugging"

clang/lib/Driver/ToolChains/Clang.cpp
7496

I think it needs enough debug info to enable MSVC stepping. The ones < DebugInfoConstructor seems not sufficient but I'm entirely sure.

llvm/lib/CodeGen/CommandFlags.cpp
469

The "-jmc-instrument" is already used by the pass itself (#define DEBUG_TYPE "jmc-instrument"). The DEBUG_TYPE one enables opt -jmc-instrument; this makes llc -enable-jmc-instrument to run the pass in IR codegen pipeline.

Just renamed cl::opt<bool> EnableJMCInstrument to cl::opt<bool> JMCInstrument.

llvm/lib/CodeGen/JMCInstrumenter.cpp
19

Agreed that in practice, it is not very likely to cause trouble. But I still think it is nice to have.

Removed the TODO. Make it a comment down below.

67

Yep, missed that. Comments updated.

76

yep. Updated the comment here.

144

End up using a lambda in runOnModule().

157

Yes, they don't change. Made them const and move them into the anonymous namespace above.

158

That's a very x86 specific check. What about other targets? (Also the calling conventions are x86 specific..)

Is32Bit -> UseFastCall to make it explicit. Only X86 should use this.

@aganea it turns out ARM should just work. I'll add tests for ARM.

169

Initially, I put this pass at the end of the mid-end optimization pipeline which may have this issue for LTO, etc. Now, this pass is in IR CodeGen pipeline. I don't think this is needed anymore. Removed.

176

Yep, used DenseMap<DISubprogram *, Constant *>.

183

Sorry for the confusion. I meant that they all go into the binary, but only one of the bytes is used at run time. Not that they are dead or stripped at linker time.

191

Removed. I was afraid that the internal-linkage flags could get removed since they have no users. But they get their address taken by external-linkage __CheckForDebuggerJustMyCode, so yeah, no need mark them used.

llvm/test/CodeGen/X86/jmc-instrument-32bit.ll
2 ↗(On Diff #404307)

There is a codegen change to make flag symbols not qualified. Hence the llc test.

Make the llc test smaller and move opt tests to llvm/test/Instrumentation.

hans added inline comments.Feb 1 2022, 6:23 AM
llvm/lib/CodeGen/CommandFlags.cpp
469

Maybe the fact that -jmc-instrument is used by the IR pass is a hint that there shouldn't be an llc option for this, then? Looking at the other options here, they're all about codegen features, whereas the instrumentation in this patch really happens at a higher level.

llvm/lib/CodeGen/JMCInstrumenter.cpp
112

Thanks, I see. Doing the same as MSVC seems fine, but I'm curious why they chose this approach.. as far as I understand, JMC will not work for the binary in this scenario, so avoiding the link error is not obviously better.

146

I still worry a bit about the target-specific code here. Normally, IR passes don't have any target-specific knowledge, but ask classes such as TargetTransformInfo for target-specific details, or possibly take them as input to the pass. For example, see llvm/lib/Transforms/CFGuard/CFGuard.cpp

I'm also not sure that lib/CodeGen/ is the right place for this pass, since most files there seem to be machine-IR passes. Maybe the natural place for this would be lib/Transforms/Instrumentation/? Is there some good pass we can compare this with?

148

This kind of mutable lambda feels pretty subtle to me. I think something like the below would be more straight-forward.

Function *CheckFunction = nullptr;
for (...) {
  ...
  if (!CheckFunction) {
    // Create the decl here.
  }
}

But if you strongly prefer the lambda, I suppose it's okay.

164

Same comment as for the lambda above. I think it would be more straight-forward to just keep the DenseMap outside the loop and just put the code to use/create the Constant in the loop.

176

Nice!

llvm/test/CodeGen/X86/jmc-instrument.ll
2 ↗(On Diff #404805)

Since it's an IR pass, I think we don't need to have an llc test at all.

ychen updated this revision to Diff 405018.Feb 1 2022, 11:36 AM
ychen marked 2 inline comments as done.
  • address comments
llvm/lib/CodeGen/CommandFlags.cpp
469

There are three kinds of passes (each in a separate pipeline), in the order of execution: IR optimization passes, IR codegen passes (some examples are here: https://github.com/llvm/llvm-project/blob/1d0244aed78114d5bd03ec4930d7687d6e587f99/llvm/include/llvm/CodeGen/MachinePassRegistry.def#L38-L51 and https://github.com/llvm/llvm-project/blob/1d0244aed78114d5bd03ec4930d7687d6e587f99/llvm/include/llvm/CodeGen/MachinePassRegistry.def#L106-L121) and MIR codegen passes. The JMC pass is an IR codegen passes. It runs within the codegen phase, but it transforms IR and it is tested using the opt tool. The llc option is for testing that the pass could be enabled using TargetOptions::JMCInstrument (clang uses this), the change in llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp and this option enables LTO+JMC with lld -mllvm -enable-jmc-instrument.

llvm/lib/CodeGen/JMCInstrumenter.cpp
146

I still worry a bit about the target-specific code here. Normally, IR passes don't have any target-specific knowledge, but ask classes such as TargetTransformInfo for target-specific details, or possibly take them as input to the pass. For example, see llvm/lib/Transforms/CFGuard/CFGuard.cpp

Understood. TargetTransformInfo is mostly for the "IR optimization passes". The JMC pass is "IR codegen passes", it is more similar to CodeGenPrepare pass than any "IR optimization passes". I think we could move the target-specific stuff into the TargetPassConfig & its derived classes, not in this patch, but the following ELF port one. WDYT?

148

yep, sounds good.

ychen added inline comments.Feb 1 2022, 5:16 PM
llvm/lib/CodeGen/JMCInstrumenter.cpp
146

I still worry a bit about the target-specific code here. Normally, IR passes don't have any target-specific knowledge, but ask classes such as TargetTransformInfo for target-specific details, or possibly take them as input to the pass. For example, see llvm/lib/Transforms/CFGuard/CFGuard.cpp

Understood. TargetTransformInfo is mostly for the "IR optimization passes". The JMC pass is "IR codegen passes", it is more similar to CodeGenPrepare pass than any "IR optimization passes". I think we could move the target-specific stuff into the TargetPassConfig & its derived classes, not in this patch, but the following ELF port one. WDYT?

Scratch that. I think this is more OS/platform-specific than target-specific. For X86, MSVC COFF and ELF are likely to have different symbol mangling and section naming preferences. And this information is pretty specific to JMC, like section name '.msvcjmc'. I think only X86 COFF has this weird mangling happen in LLVM codegen instead of the frontend. For non-x86 COFF and ELF, the handling is pretty much the same. So it may not be worth the effort of putting small pieces of OS/platform-specific information elsewhere?

hans added inline comments.Feb 2 2022, 5:45 AM
llvm/lib/CodeGen/CommandFlags.cpp
469

Thanks for the pointer, the IR codegen passes is something I'm not very familiar with.

Just looking at a few of the first ones you linked to, I see that they live under lib/Transforms/. And when I look in lib/CodeGen/ the vast majority of those work on MachineFunctions -- but not all. So I'm not really sure how we decide what should go where?

I think the best way is to look for a similar pass and see how that works. Maybe llvm/lib/Transforms/CFGuard/CFGuard.cpp could be a good example, since it's also inserting Windows-specific instrumentation at the IR level, similar to this pass.

llvm/lib/CodeGen/JMCInstrumenter.cpp
61

nit: this could be

const char CheckFunctionName[] = "..."
159

Having both Flag and TheFlag might be confusing. Could we rely on the DenseMap default-constructing values on lookup and do:

Constant *&Flag = SavedFlags[SP];
if (!Flag) {
  ...
  Flag = ...
}
ychen updated this revision to Diff 405406.Feb 2 2022, 1:16 PM
ychen marked an inline comment as done.
  • Address feeback
llvm/lib/CodeGen/CommandFlags.cpp
469

Looking into the CFGuard passes, I think they are all "IR codegen passes". I have no idea why they need to stay in lib/Transform (and in their own library LLVMCFGuard) instead of in lib/CodeGen. I would argue that they should probably be in lib/CodeGen instead. Hi @rnk , any idea about this?

llvm/lib/CodeGen/JMCInstrumenter.cpp
159

yep, this is better. Thanks.

rnk added inline comments.Feb 2 2022, 5:44 PM
llvm/lib/CodeGen/JMCInstrumenter.cpp
146

I think the existing PGO passes do a variety of target-specific things, and they live in lib/Transforms/Instrumentation. For example, they pick different section names for ELF and COFF.

This seems like the function entry/exit instrumentation, and I wonder if it should be added as part of the CodeGenPassBuilder.h list of passes.

ychen updated this revision to Diff 405520.Feb 2 2022, 9:48 PM
  • Remove a file that is included by accident.
ychen added inline comments.Feb 2 2022, 10:08 PM
llvm/lib/CodeGen/CommandFlags.cpp
469

Any objection if I keep the JMC pass in CodeGen and move CFGuard pass to CodeGen in a separate patch?

llvm/lib/CodeGen/JMCInstrumenter.cpp
146

> This seems like the function entry/exit instrumentation, and I wonder if it should be added as part of the CodeGenPassBuilder.h list of passes.

Yeah, it should. CodeGenPassBuilder.h is already lagging behind TargetPassConfig by far. I could take care of it when making CodeGenPassBuilder.h have parity with TargetPassConfig in terms of all lit tests.

hans added a comment.Feb 3 2022, 1:42 AM

It seems Phabricator ate my comment, but I meant to say:

My understanding is still that IR passes generally live in Transforms/ and that CodeGen/ deals with lower levels such as MachineInstrs, SelectionDAG, etc. with CodegenPrepare as the big exception.

Thinking about it another way, what about this pass makes it a "codegen IR" pass as opposed to all the other passes that add instrumentation to the IR and live in Transforms/Instrumentation/ ?

ychen added a comment.Feb 3 2022, 10:46 AM

It seems Phabricator ate my comment, but I meant to say:

My understanding is still that IR passes generally live in Transforms/ and that CodeGen/ deals with lower levels such as MachineInstrs, SelectionDAG, etc. with CodegenPrepare as the big exception.

Thinking about it another way, what about this pass makes it a "codegen IR" pass as opposed to all the other passes that add instrumentation to the IR and live in Transforms/Instrumentation/ ?

The passes in lib/Transforms/Instrumentation runs with EmitAssemblyHelper::RunOptimizationPipeline. JMC pass runs with EmitAssemblyHelper::RunCodegenPipeline.

rnk added a comment.Feb 3 2022, 1:12 PM

The passes in lib/Transforms/Instrumentation runs with EmitAssemblyHelper::RunOptimizationPipeline. JMC pass runs with EmitAssemblyHelper::RunCodegenPipeline.

Sure, but that's a choice. Most of the instrumentation passes (ASan) are also designed to run late to avoid interfering with middle end optimization. Is there a specific reason that JMC has to be a codegen pass? Maybe you've already mentioned it, apologies if so.

As for whether this should be addressed in a follow-up, I think it's best to make sure we have the right library structure first, and then iterate on the implementation.

ychen added a comment.Feb 3 2022, 1:26 PM

The passes in lib/Transforms/Instrumentation runs with EmitAssemblyHelper::RunOptimizationPipeline. JMC pass runs with EmitAssemblyHelper::RunCodegenPipeline.

Sure, but that's a choice. Most of the instrumentation passes (ASan) are also designed to run late to avoid interfering with middle end optimization. Is there a specific reason that JMC has to be a codegen pass? Maybe you've already mentioned it, apologies if so.

The instrumentation is per-function, ideally for each function that has debuginfo and ends up in the executable. So I want it to happen the last in the IR codegen pipeline (target could arrange additional IR passes, they may duplicate functions or anything). Making it codegen pass also makes it LTO-friendly, otherwise, I need to make sure the pass runs once. The JMC pass could be a machine-independent MIR pass too, but I think there is not much benefit for that.

rnk added a comment.Feb 3 2022, 1:33 PM

The instrumentation is per-function, ideally for each function that has debuginfo and ends up in the executable. So I want it to happen the last in the IR codegen pipeline (target could arrange additional IR passes, they may duplicate functions or anything). Making it codegen pass also makes it LTO-friendly, otherwise, I need to make sure the pass runs once. The JMC pass could be a machine-independent MIR pass too, but I think there is not much benefit for that.

I would argue that function passes shouldn't do function duplication, but being LTO friendly is a pretty good reason to want to do instrumentation as part of the codegen pipeline. We have encountered problems with sanitizer passes running twice in LTO scenarios, and it's annoying to deal with.

So, I think that's a compelling reason to go with your current structure.

hans added inline comments.Feb 8 2022, 10:28 AM
llvm/lib/CodeGen/JMCInstrumenter.cpp
181

might as well get the Function* already here?

CheckFunction = cast<Function>(M.getOrInsertFunction(...).getCallee());
llvm/lib/CodeGen/TargetPassConfig.cpp
1096 ↗(On Diff #405520)

could this be moved into addIRPasses()?

llvm/test/CodeGen/X86/jmc-instrument.ll
2 ↗(On Diff #404805)

I still think it's unusual to have an llc test for an IR pass. Are there any other examples where we do something similar?

ychen marked an inline comment as done.Feb 8 2022, 12:11 PM
ychen added inline comments.
llvm/lib/CodeGen/JMCInstrumenter.cpp
181

sounds good.

llvm/lib/CodeGen/TargetPassConfig.cpp
1096 ↗(On Diff #405520)

Yes, it could. CFGuardCheckPass is there for targets with COFF. I didn't put it there because I plan to make it work with ELF in the next step, putting it here is convenient. I don't feel strongly though.

llvm/test/CodeGen/X86/jmc-instrument.ll
2 ↗(On Diff #404805)

yes, it is pretty rare. I wanted to test the CodeViewDebug.cpp change, however, the code path only triggers when the JMC pass is run. Using llc came to mind. I'm open to any alternative.

ychen updated this revision to Diff 406927.Feb 8 2022, 12:17 PM
ychen marked an inline comment as done.
  • address feedback
hans added inline comments.Feb 9 2022, 4:58 AM
llvm/lib/CodeGen/TargetPassConfig.cpp
1096 ↗(On Diff #405520)

I think it would be better to move it to addIRPasses().

llvm/test/CodeGen/X86/jmc-instrument.ll
2 ↗(On Diff #404805)

Oh, I see. But does the test check the right thing, then? I only see CHECKs for instructions, not debug info.

And isn't it enough to use IR with a global variable in the .msvcjmc section as input? I think the tighter the test, the better.

ychen updated this revision to Diff 407384.Feb 9 2022, 8:49 PM
ychen marked an inline comment as done.
  • 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.
ychen marked an inline comment as done.Feb 9 2022, 8:53 PM
ychen added inline comments.
llvm/test/CodeGen/X86/jmc-instrument.ll
2 ↗(On Diff #404805)

It turns out that the CodeViewDebug.cpp change is not needed anymore. Delete this test too. Thanks for help spotting this!

hans accepted this revision.Feb 10 2022, 2:10 AM

lgtm

This revision is now accepted and ready to land.Feb 10 2022, 2:10 AM
This revision was landed with ongoing or failed builds.Feb 10 2022, 10:26 AM
This revision was automatically updated to reflect the committed changes.
ychen marked an inline comment as done.

Hi,

We have two failing test cases on Fuchsia's clang canary builder on Windows x64.

LLVM :: Instrumentation/JustMyCode/jmc-instrument-x86.ll
LLVM :: Instrumentation/JustMyCode/jmc-instrument.ll

First seen here: https://luci-milo.appspot.com/ui/p/fuchsia/builders/toolchain.ci/clang-windows-x64/b8822587673277278177/overview

These are JustMyCode tests, added in this patch, and it appears these tests may need to be adjusted.

You can find the full output in the linked builders, but here is a sample output from one of the tests. It seems to me like the lit file may just need to be adjusted slightly?

Script:
--
: 'RUN: at line 1';   c:\b\s\w\ir\x\w\staging\llvm_build\bin\opt.exe -jmc-instrument -S < C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll | c:\b\s\w\ir\x\w\staging\llvm_build\bin\filecheck.exe C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll
--
Exit Code: 1

Command Output (stdout):
--
$ ":" "RUN: at line 1"
$ "c:\b\s\w\ir\x\w\staging\llvm_build\bin\opt.exe" "-jmc-instrument" "-S"
$ "c:\b\s\w\ir\x\w\staging\llvm_build\bin\filecheck.exe" "C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll"
# command stderr:
C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll:5:10: error: CHECK: expected string not found in input
; CHECK: @"_A85D9D03_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0
         ^
<stdin>:6:34: note: scanning from here
$_JustMyCode_Default = comdat any
                                 ^
<stdin>:8:1: note: possible intended match here
@"_A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0
^

Input file: <stdin>
Check file: C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll

-dump-input=help explains the following input dump.

Input was:
<<<<<<
           1: ; ModuleID = '<stdin>' 
           2: source_filename = "<stdin>" 
           3: 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" 
           4: target triple = "i386-pc-windows-msvc" 
           5:  
           6: $_JustMyCode_Default = comdat any 
check:5'0                                      X error: no match found
           7:  
check:5'0     ~
           8: @"_A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
check:5'1     ?                                                                                           possible intended match
           9: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @_JustMyCode_Default to i8*)], section "llvm.metadata" 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          10:  
check:5'0     ~
          11: define void @w1() #0 !dbg !10 { 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          12:  call x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef @"_A8764FDD_x@c") 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          13:  ret void 
check:5'0     ~~~~~~~~~~
           .
           .
           .
>>>>>>

error: command failed with exit status: 1

--

If fixing the test will take a long time, can you revert until one is ready?

ychen added a comment.Feb 10 2022, 2:18 PM

Hi,

We have two failing test cases on Fuchsia's clang canary builder on Windows x64.

LLVM :: Instrumentation/JustMyCode/jmc-instrument-x86.ll
LLVM :: Instrumentation/JustMyCode/jmc-instrument.ll

First seen here: https://luci-milo.appspot.com/ui/p/fuchsia/builders/toolchain.ci/clang-windows-x64/b8822587673277278177/overview

These are JustMyCode tests, added in this patch, and it appears these tests may need to be adjusted.

You can find the full output in the linked builders, but here is a sample output from one of the tests. It seems to me like the lit file may just need to be adjusted slightly?

Script:
--
: 'RUN: at line 1';   c:\b\s\w\ir\x\w\staging\llvm_build\bin\opt.exe -jmc-instrument -S < C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll | c:\b\s\w\ir\x\w\staging\llvm_build\bin\filecheck.exe C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll
--
Exit Code: 1

Command Output (stdout):
--
$ ":" "RUN: at line 1"
$ "c:\b\s\w\ir\x\w\staging\llvm_build\bin\opt.exe" "-jmc-instrument" "-S"
$ "c:\b\s\w\ir\x\w\staging\llvm_build\bin\filecheck.exe" "C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll"
# command stderr:
C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll:5:10: error: CHECK: expected string not found in input
; CHECK: @"_A85D9D03_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0
         ^
<stdin>:6:34: note: scanning from here
$_JustMyCode_Default = comdat any
                                 ^
<stdin>:8:1: note: possible intended match here
@"_A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0
^

Input file: <stdin>
Check file: C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll

-dump-input=help explains the following input dump.

Input was:
<<<<<<
           1: ; ModuleID = '<stdin>' 
           2: source_filename = "<stdin>" 
           3: 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" 
           4: target triple = "i386-pc-windows-msvc" 
           5:  
           6: $_JustMyCode_Default = comdat any 
check:5'0                                      X error: no match found
           7:  
check:5'0     ~
           8: @"_A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
check:5'1     ?                                                                                           possible intended match
           9: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @_JustMyCode_Default to i8*)], section "llvm.metadata" 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          10:  
check:5'0     ~
          11: define void @w1() #0 !dbg !10 { 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          12:  call x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef @"_A8764FDD_x@c") 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          13:  ret void 
check:5'0     ~~~~~~~~~~
           .
           .
           .
>>>>>>

error: command failed with exit status: 1

--

If fixing the test will take a long time, can you revert until one is ready?

Reverted in b380a31de084a540cfa38. Thanks. I'll take a look.

ychen added a comment.Feb 15 2022, 1:09 PM

Hi,

We have two failing test cases on Fuchsia's clang canary builder on Windows x64.

LLVM :: Instrumentation/JustMyCode/jmc-instrument-x86.ll
LLVM :: Instrumentation/JustMyCode/jmc-instrument.ll

First seen here: https://luci-milo.appspot.com/ui/p/fuchsia/builders/toolchain.ci/clang-windows-x64/b8822587673277278177/overview

These are JustMyCode tests, added in this patch, and it appears these tests may need to be adjusted.

You can find the full output in the linked builders, but here is a sample output from one of the tests. It seems to me like the lit file may just need to be adjusted slightly?

Script:
--
: 'RUN: at line 1';   c:\b\s\w\ir\x\w\staging\llvm_build\bin\opt.exe -jmc-instrument -S < C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll | c:\b\s\w\ir\x\w\staging\llvm_build\bin\filecheck.exe C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll
--
Exit Code: 1

Command Output (stdout):
--
$ ":" "RUN: at line 1"
$ "c:\b\s\w\ir\x\w\staging\llvm_build\bin\opt.exe" "-jmc-instrument" "-S"
$ "c:\b\s\w\ir\x\w\staging\llvm_build\bin\filecheck.exe" "C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll"
# command stderr:
C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll:5:10: error: CHECK: expected string not found in input
; CHECK: @"_A85D9D03_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0
         ^
<stdin>:6:34: note: scanning from here
$_JustMyCode_Default = comdat any
                                 ^
<stdin>:8:1: note: possible intended match here
@"_A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0
^

Input file: <stdin>
Check file: C:\b\s\w\ir\x\w\llvm-llvm-project\llvm\test\Instrumentation\JustMyCode\jmc-instrument-x86.ll

-dump-input=help explains the following input dump.

Input was:
<<<<<<
           1: ; ModuleID = '<stdin>' 
           2: source_filename = "<stdin>" 
           3: 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" 
           4: target triple = "i386-pc-windows-msvc" 
           5:  
           6: $_JustMyCode_Default = comdat any 
check:5'0                                      X error: no match found
           7:  
check:5'0     ~
           8: @"_A8764FDD_x@c" = internal unnamed_addr global i8 1, section ".msvcjmc", align 1, !dbg !0 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
check:5'1     ?                                                                                           possible intended match
           9: @llvm.used = appending global [1 x i8*] [i8* bitcast (void (i8*)* @_JustMyCode_Default to i8*)], section "llvm.metadata" 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          10:  
check:5'0     ~
          11: define void @w1() #0 !dbg !10 { 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          12:  call x86_fastcallcc void @__CheckForDebuggerJustMyCode(i8* inreg noundef @"_A8764FDD_x@c") 
check:5'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          13:  ret void 
check:5'0     ~~~~~~~~~~
           .
           .
           .
>>>>>>

error: command failed with exit status: 1

--

If fixing the test will take a long time, can you revert until one is ready?

Reverted in b380a31de084a540cfa38. Thanks. I'll take a look.

Relanded in f927021410691bc261