Description
With the following C file (src/use_macro.c
):
__asm__(".include \"macro.s\"");
void my_function_1(void) {
__asm__ volatile("foo 3");
}
void my_function_2(void) {
__asm__ volatile("foo 4");
}
And the following include/macro.s
file:
.macro foo bar
// It doesn't really matter what goes here, this is just an example of an assembly macro
// that the user wants to invoke in multiple inline assembly blocks in a C file
li a0, \bar
call validate
.endm
The following command works, and the produced file has indeed invoked the assembly macro correctly:
$ clang --target=riscv32-unknown-none-elf -I ./include -c ./src/use_macro.c
But if you add -flto
, there is an include path error:
$ clang --target=riscv32-unknown-none-elf -I ./include -c ./src/use_macro.c -flto
<inline asm>:1:10: error: Could not find include file 'macro.s'
1 | .include "macro.s"
| ^
1 error generated.
I think I know where clang/LLVM is going wrong, but I'm not sure how to get the right information to the right place to fix it. I think the bug is in target-independent code, and this would reproduce with other targets.
At the end of the (full-)LTO compiler (not linker) pipeline, the module has to be turned into bitcode BitcodeWriterPass
, which requires a ModuleSummary (generated in ModuleSummaryIndexAnalysis
), which in turn requires scanning the inline assembly for if it has generated symbols, in ModuleSymbolTable::CollectAsmSymbols
.
I think the problem here is in initializeRecordStreamer
, which seems to create a brand new MCTargetOptions
(as well as a lot of other target-specific objects which are used when parsing the inline assembly).
In a non-LTO compile (which works), AsmPrinter::emitInlineAsm
uses the options in kept in TM.Options.MCOptions
, but evidently this pass is doing a lot initialisation of the target directly in the AsmPrinter.
Part of the issue does seem to be the use of .include
in a piece of global inline assembly - putting it in one of the in-function inline assembly blocks does seem to at least let me produce bitcode,
I haven't investigated exactly where the problem comes from in thin LTO, but the compiler hits the same error as it does with full LTO.
This is not the only path issue like this in the LTO pipeline, as I'm not sure there's a way to pass an include path correctly to the linker, but I think the solution to this one will indicate the way to go to solve that issue too. You can get this error if you move the .include
into one of the in-function inline assembly blocks, which gets rid of the issue at compile time but brings back the same thing at link time.