ajpaverd marked 9 inline comments as done. ajpaverd added inline comments.
================ Comment at: clang/include/clang/Driver/Options.td:500 def b : JoinedOrSeparate<["-"], "b">, Flags<[Unsupported]>; +def cfguard_no_checks : Flag<["-"], "cfguard-no-checks">, Flags<[CC1Option]>, + HelpText<"Emit Windows Control Flow Guard tables only (no checks).">; ---------------- hans wrote: > rnk wrote: > > @hans, WDYT about the -cc1 interface? I think this is fine. > The -cc1 interface looks good to me, but since these are cc1 options only, I > think they should be in CC1Options.td, not Options.td (I realize this is a > pre-existing issue with -cfguard). Thanks for all the helpful feedback @hans. I think I've addressed all your comments in the latest revision. ================ Comment at: llvm/lib/CodeGen/CFGuardLongjmp.cpp:101 + // targets. + for (MachineInstr *Setjmp : SetjmpCalls) { + MachineBasicBlock *OldMBB = Setjmp->getParent(); ---------------- rnk wrote: > Rather than changing the machine CFG, I would suggest using > MachineInstr::setPostInstrSymbol, which I've been planning to use for exactly > this purpose. :) You can make labels with MCContext::createSymbol, and you'll > want to come up with symbols that won't clash with C or C++ symbols. I see > MSVC makes labels like `$LN4`, so maybe `$cfgsjN` or something like that. I > think MCContext will add numbers for you. Thanks for the suggestion! It looks like MCContext::createSymbol is private, so I'm generating a new symbol name and number for MCContext::getOrCreateSymbol. Does this look OK? ================ Comment at: llvm/lib/Target/X86/X86FixupCFGuard.cpp:13 +/// for such cases and replaces the pair of instructions with a single +/// call/invoke. For example: +/// ---------------- rnk wrote: > hans wrote: > > Naive question: Why do we generate code as in the examples in the first > > place, and can't some general optimization pass do this folding? From the > > examples it looks like straight-forward constant propagation. > Actually, I used this test IR, LLVM seems to always fold the memory operand > into the call: > ``` > @fptr = external dso_local global void()* > define i32 @foo() { > %fp1 = load void()*, void()** @fptr > call void %fp1() > %fp2 = load void()*, void()** @fptr > call void %fp2() > ret i32 0 > } > ``` > > Maybe it won't do it if there are more parameters, I'm not sure. > > I ran llc with both isels for x64 and ia32, and it always folded the load > into the call. Maybe it's best to make this a verification pass that emits an > error via MCContext if there is an unfolded load of the CFG check function > pointer? I'm seeing this when compiling with optimizations disabled. When I run llc with `-fast-isel=false`, the following slightly modified IR does not fold the memory operand into the call: ``` @fptr = external dso_local global void()* define i32 @foo() #0 { %fp1 = load void()*, void()** @fptr call void %fp1() %fp2 = load void()*, void()** @fptr call void %fp2() ret i32 0 } attributes #0 = { noinline optnone } ``` It looks like this is caused by checks for `OptLevel == CodeGenOpt::None` in: - SelectionDAGISel::IsLegalToFold - X86DAGToDAGISel::IsProfitableToFold - X86DAGToDAGISel::PreprocessISelDAG (in this case OptLevel != CodeGenOpt::None) I guess this is not high priority as it only happens at -O0. Should I look into enabling these specific optimizations when CFG is enabled, or just emit a warning when this happens? ================ Comment at: llvm/lib/Transforms/CFGuard/CFGuard.cpp:254 + // register (i.e. RAX on 64-bit X86 targets) + GuardDispatch->setCallingConv(CallingConv::CFGuard_Dispatch); + ---------------- rnk wrote: > So, this isn't going to work if the original calling convention was something > other than the default. For x64, the only commonly used non-standard > convention would be vectorcall. Changing the convention here in the IR is > going to make it hard to pass that along. > > I think as you can see from how much code you have here already, replacing > call instructions in general is really hard. These days there are also > bundles, which I guess is something else missing from the above. > > Here's a sketch of an alternative approach: > - load __guard_dispatch_icall_fptr as normal > - get the pre-existing function type of the callee > - cast the loaded pointer to the original function pointer type > - use `CB->setCalledOperand` to replace the call target > - add the original call target as an "argument bundle" to the existing > instruction > - change SelectionDAGBuilder.cpp to recognize the new bundle kind and create > a new ArgListEntry in the argument list > - make and set a new bit in ArgListEntry, something like isCFGTarget > - change CC_X86_Win64_C to put CFGTarget arguments in RAX, similar to how > CCIfNest puts things in R10 > > This way, the original call instruction remains with exactly the same > arrangement of args, attributes, callbr successors, tail call kind, etc. All > you're doing is changing the callee, and passing the original callee as an > extra argument on the side. Basically, this approach leverages bundles to > avoid messing with the function type, which more or less requires replacing > the call. :) > > There are probably issues with this design that I haven't foreseen, but I > think it's worth a try. This design seems to work well. I guess at some point we would also have to teach GlobalISel to recognize these operand bundles, right? ================ Comment at: llvm/lib/Transforms/CFGuard/CFGuard.cpp:207 + + // Create a copy of the call/invoke instruction and add the new bundle. + CallBase *NewCB; ---------------- Is this the correct way to add operand bundles to what is essentially an existing call/invoke instruction? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65761/new/ https://reviews.llvm.org/D65761 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits