1

I am writing a MachineFunctionPass targeting the X86 architecture which results in a modified llc binary.

In order to test my modified version of llc I have created a bunch of .c programs whose MIR will be handled by my pass.

For the sake of cleanliness, I have added the directory including the sources directly into LLVM's source tree, specifically in $llvm_src_dir/lib/Target/X86/$examples_dir: I have then plugged it into LLVM build system by appending the add_subdirectory() directive to $llvm_src_dir/lib/Target/X86/CMakeLists.txt.

In this way, I will be able to build everything directly from LLVM's build directory.

Now: how do I specify in my $examples_dir/CMakeLists.txt to use LLVM's in-tree llc?

Source tree structure

This is the sources' directory structure. I have omitted all the root's children directories since I've only included the "interesting ones".

LLVM's defines the llc target in tools/llc while my sources live quite deeper in the directory as shown in the following tree:

llvm_src_dir
├── bindings
├── cmake
├── docs
├── examples
├── include
├── lib
    └── Target
        └── X86
            /* 
             * My git repo is here. LLVM's and
             * my MachineFunctionPass' files
             * live here 
            */
            ├── .git
            ├── CMakeLists.txt // This is LLVM's X86 CMakeLists.txt
            └── examples
                └── CMakeLists.txt // My CMakeLists.txt
├── projects
├── resources
├── runtimes
├── test
├── tools
    └── llc
        └── CMakeLists.txt // this is where LLVM's llc target is defined
├── unittests
└── utils

lib/Target/X86/CMakeLists.txt

This is how I edited the CMakeLists.txt of the architecture I'm targeting:

set (CMAKE_CXX_STANDARD 14)
set(LLVM_TARGET_DEFINITIONS X86.td)

tablegen(LLVM X86GenAsmMatcher.inc -gen-asm-matcher)
tablegen(LLVM X86GenAsmWriter.inc -gen-asm-writer)
tablegen(LLVM X86GenAsmWriter1.inc -gen-asm-writer -asmwriternum=1)
tablegen(LLVM X86GenCallingConv.inc -gen-callingconv)
tablegen(LLVM X86GenDAGISel.inc -gen-dag-isel)
tablegen(LLVM X86GenDisassemblerTables.inc -gen-disassembler)
tablegen(LLVM X86GenEVEX2VEXTables.inc -gen-x86-EVEX2VEX-tables)
tablegen(LLVM X86GenFastISel.inc -gen-fast-isel)
tablegen(LLVM X86GenGlobalISel.inc -gen-global-isel)
tablegen(LLVM X86GenInstrInfo.inc -gen-instr-info)
tablegen(LLVM X86GenRegisterBank.inc -gen-register-bank)
tablegen(LLVM X86GenRegisterInfo.inc -gen-register-info)
tablegen(LLVM X86GenSubtargetInfo.inc -gen-subtarget)

if (X86_GEN_FOLD_TABLES)
    tablegen(LLVM X86GenFoldTables.inc -gen-x86-fold-tables)
endif ()

add_public_tablegen_target(X86CommonTableGen)

set(MY_SOURCES
        a.cpp
        b.cpp
        c.cpp
        )

set(sources
        ShadowCallStack.cpp
        X86AsmPrinter.cpp
        X86CallFrameOptimization.cpp
        X86CallingConv.cpp
        X86CallLowering.cpp
        X86CmovConversion.cpp
        X86DomainReassignment.cpp
        X86ExpandPseudo.cpp
        X86FastISel.cpp
        X86FixupBWInsts.cpp
        X86FixupLEAs.cpp
        X86AvoidStoreForwardingBlocks.cpp
        X86FixupSetCC.cpp
        X86FlagsCopyLowering.cpp
        X86FloatingPoint.cpp
        X86FrameLowering.cpp
        X86InstructionSelector.cpp
        X86ISelDAGToDAG.cpp
        X86ISelLowering.cpp
        X86IndirectBranchTracking.cpp
        X86InterleavedAccess.cpp
        X86InstrFMA3Info.cpp
        X86InstrFoldTables.cpp
        X86InstrInfo.cpp
        X86EvexToVex.cpp
        X86LegalizerInfo.cpp
        X86MCInstLower.cpp
        X86MachineFunctionInfo.cpp
        X86MacroFusion.cpp
        X86OptimizeLEAs.cpp
        X86PadShortFunction.cpp
        X86RegisterBankInfo.cpp
        X86RegisterInfo.cpp
        X86RetpolineThunks.cpp
        X86SelectionDAGInfo.cpp
        X86ShuffleDecodeConstantPool.cpp
        X86SpeculativeLoadHardening.cpp
        X86Subtarget.cpp
        X86TargetMachine.cpp
        X86TargetObjectFile.cpp
        X86TargetTransformInfo.cpp
        X86VZeroUpper.cpp
        X86WinAllocaExpander.cpp
        X86WinEHState.cpp
        ${MY_SOURCES}
        )

add_llvm_target(X86CodeGen ${sources})

add_subdirectory(AsmParser)
add_subdirectory(Disassembler)
add_subdirectory(InstPrinter)
add_subdirectory(MCTargetDesc)
add_subdirectory(TargetInfo)
add_subdirectory(Utils)
add_subdirectory(examples) // my examples directory

What I have already tried

I am currently using find_path() to find llc but this requires llc to be already compiled and therefore my examples CMakeLists.txt will fail the validation if I don't compile llc beforehand.

Assuming the path exists, I finally use an add_custom_command() directive to use llc in my CMakeLists.txt but this is way too hacky in my opinion.

Basically, I need to add the llc target as a dependency for my targets and then use llc's path to compile my examples' .bc files into .s.

Any ideas?

Thank you very much!

peperunas
  • 428
  • 1
  • 6
  • 17
  • I've never tried it, but I think you could use `try_compile` with manually specified path to new `llc`. Also, do you want to use ctest to run tests? – R2RT May 31 '19 at 10:47
  • Thanks for your reply! I specifically want to avoid to set manual paths because LLVM's build directory might change from user to user. I don't really need ctest right now since these are just silly examples that may be gone soon. – peperunas May 31 '19 at 10:48
  • By manually I mean "evaluated by cmake upon configuration, but manually listed by you in `try_compile` arguments". Let me try do proof of concept – R2RT May 31 '19 at 10:51
  • Do you expect just to call `llc test1.bc` or do you have more sophisticated command line arguments? – R2RT May 31 '19 at 11:09
  • I've shown an example to convert `.bc`->`.s`, but I am not sure if that is whole command chain needed. – R2RT May 31 '19 at 11:54

1 Answers1

1

I see two possible solutions and for now let me present simpler one.

project(nested-toolchain C CXX)

# Assume that `llc` target is created somewhere within project
# Even if it is created in later `add_subdirectory` calls,
# We can defer evaluation to its path using generator expression $<TARGET_FILE:llc>

# This is the magic.
# It tells cmake how to produce test1.s from test1.bc using llc binary
# Also will track test1.bc changes and set test1.s as dirty when needed
add_custom_command(OUTPUT test1.s COMMAND $<TARGET_FILE:llc> test1.bc DEPENDS test1.bc)
add_custom_command(OUTPUT test2.s COMMAND $<TARGET_FILE:llc> test2.bc DEPENDS test2.bc)

# Now merge custom commands into single target which can be called by make/ninja/...
# simply call `make tests` to run two commands listed above (and compile llc before that)
add_custom_target(tests SOURCES test1.s test2.s)

To sum up: first we know that our CMake project can produce llc binary somewhere from llvm-sources. This binary can be used to produce test.s files with magic command specified. They depend on corresponding .bc files. These .bc files are joined into single target tests via add_custom_target.

I've used add_custom_target to keep example minimal and it has one flaw: calling make tests will always call all llc commands, as custom targets are always considered "out of date".

If you want to use another tool over .s files, I recommend to chain yet another add_custom_command analogically and use add_custom_target to finish the chain.

This approach should work as long as you are testing single binary (llc). If you wanted to test whole toolchain, I'd go for try_compile.

For completeness, for llc.cpp file as given:

// Just print args
#include <iostream>
int main(int argc, char **argv) {
  for (int i = 0; i < argc; i++) {
    std::cout << argv[i] << ' ';
  }
  std::cout << "\n";
  return 0;
}

ninja tests result in:

$ ninja tests
[1/2] Generating test2.s
/home/stackoverflow/nested-toolchain/build/llc test2.bc 
[2/2] Generating test1.s
/home/stackoverflow/nested-toolchain/build/llc test1.bc 
R2RT
  • 2,061
  • 15
  • 25
  • Yep. My code will work if you replace `myllc` with simple `llc`. Maybe `my` prefix is not neede here... But for sure I recommend to make alias for this target to be sure that your compiled `llc` is used, not system's one. – R2RT May 31 '19 at 12:28
  • Thank you very much for your answer. The issue is that LLVM already has its own `CMakeLists.txt` for `llc` so I think there shouldn't be the need to add another `add_executable()` directive. On top of that, I would like not to modify LLVM's `CMakeLists.txt` files that are not the one in `lib/Target/X86` since that's the location where I have my git repository. EDIT: Let me update my question with more information about the directory structure since that may help a bit more. – peperunas May 31 '19 at 12:29
  • @peperunas I've renamed `myllc` into `llc` and assumed it comes from `add_subdirectory`. Still works :) – R2RT May 31 '19 at 12:35
  • @peperunas And there is no need to modify LLVM's sources, everything done here is in your `CMakeLists.txt`. – R2RT May 31 '19 at 12:37
  • I have updated the question with more information about the directory structure. – peperunas May 31 '19 at 12:54
  • @peperunas I don't think it anyhow affects my code. Simply remove `add_subdirectory(llvm-sources)` from code I've posted – R2RT May 31 '19 at 13:15
  • I'm sorry, you're right, my bad. Unfortunately, the alias doesn't work since CMake can't find the `llc` target, strange. `add_executable cannot create ALIAS target "llvm::llc" because target "llc" does not already exist.` The thing is that the executable is created in `llc`'s `CMakeLists.txt` through a custom `add_llvm_tool()` directive which eventually executes the standard `add_executable()`. – peperunas May 31 '19 at 13:25
  • @peperunas Apperently the order of `add_subdirectory` in root directory matters... So we cannot add ALIASed target, but it can be worked out with generator expression: `$` in place of `llvm::llc`. See edited answer – R2RT May 31 '19 at 13:32
  • "Full path to main file (.exe, .so.1.2, .a) where tgt is the name of a target." https://cmake.org/cmake/help/v3.14/manual/cmake-generator-expressions.7.html#target-dependent-queries Thus to `llc` built binary @peperunas – R2RT May 31 '19 at 13:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/194251/discussion-between-peperunas-and-r2rt). – peperunas May 31 '19 at 13:43