ThinLTO: module-scope inline assembly blocks

Hi all,
I am trying to add ThinLTO to the LDC compiler. It seems to work well on Mac (XCode 8) and Ubuntu (ld.gold + LLVMgold plugin).
However, I am running into trouble with module-scope inline assembly blocks.

I have a module ASM with a function foo defined in an inline assembly block (and an LLVM IR declare @foo() for it). There is also a “normal” function void simplefunction() defined in the module.

module asm "\09.text"
module asm "\09.globl\09foo"
module asm "\09.align\0916, 0x90"
module asm "\09.type\09foo,@function"
module asm "foo:"
module asm "\09movq %rdi, %rax"
module asm "\09rorw $8, %ax"
module asm "\09ret "
module asm "\09.size\09foo, .-foo"
module asm ""

declare zeroext i16 @foo(i16 zeroext) #0

define i32 @_simplefunction() #1 {
ret i32 1
}

Another module MAIN contains a function that calls ASM’s simplefunction().

define i32 @_Dmain({ i64, { i64, i8* }* } %unnamed) #0 {
%1 = call i32 @_simplefunction() #1
ret i32 %1
}
declare i32 @_simplefunction() #1

Then these two modules are passed to the linker, using ThinLTO, thus bitcode files with .o extension and module summaries added. The linker reports a multiple definition error for the inline assembly function.
(note that foo is not called anywhere)

I suspect that when ThinLTO decides to import a function from another module for inlining, the other module’s module-scope assembly blocks are also imported, which later result in the multiple definition problems.

My question: is this known, intended, or otherwise broken on my end? Or perhaps is it a bug and importing from modules with module-scope assembly should be disabled / should not import the module-scope asm?

(I can “fix” things on my end by disabling ThinLTO when there is a module-scope asm block, but perhaps there should be a proper fix in LLVM)

Thanks!

Cheers,
Johan

Hi all,
  I am trying to add ThinLTO to the LDC compiler. It seems to work well on
Mac (XCode 8) and Ubuntu (ld.gold + LLVMgold plugin).
However, I am running into trouble with module-scope inline assembly
blocks.

Are you adding the support using the new LTO API or using the libLTO C
interfaces?

I have a module ASM with a function `foo` defined in an inline assembly
block (and an LLVM IR `declare @foo()` for it). There is also a "normal"
function `void simplefunction()` defined in the module.

module asm "\09.text"
module asm "\09.globl\09foo"
module asm "\09.align\0916, 0x90"
module asm "\09.type\09foo,@function"
module asm "foo:"
module asm "\09movq %rdi, %rax"
module asm "\09rorw $8, %ax"
module asm "\09ret "
module asm "\09.size\09foo, .-foo"
module asm ""

declare zeroext i16 @foo(i16 zeroext) #0

define i32 @_simplefunction() #1 {
  ret i32 1
}

Another module MAIN contains a function that calls ASM's
`simplefunction()`.

define i32 @_Dmain({ i64, { i64, i8* }* } %unnamed) #0 {
  %1 = call i32 @_simplefunction() #1
  ret i32 %1
}
declare i32 @_simplefunction() #1

Then these two modules are passed to the linker, using ThinLTO, thus
bitcode files with .o extension and module summaries added. The linker
reports a multiple definition error for the inline assembly function.
(note that `foo` is not called anywhere)

I suspect that when ThinLTO decides to import a function from another
module for inlining, the other module's module-scope assembly blocks are
also imported, which later result in the multiple definition problems.

My question: is this known, intended, or otherwise broken on _my_ end? Or
perhaps is it a bug and importing from modules with module-scope assembly
should be disabled / should not import the module-scope asm?

(I can "fix" things on my end by disabling ThinLTO when there is a
module-scope asm block, but perhaps there should be a proper fix in LLVM)

I think this should just work, since module-level assembly is parsed when
reading bitcode, and if the global foo is imported it should end up with
available_externally linkage which would cause the def to be dropped if it
wasn't inlined. Not sure what is going wrong.

Is there a save-temps option for the LDC linker that you could dump the IR
after importing? With the new LTO API you can use the Config::addSaveTemps
function to enable this. With the old LTO support used by libLTO there is
an addSaveTempsDir on ThinLTOCodeGenerator, which looks like it can be set
via thinlto_codegen_set_savetemps_dir.

Teresa

Thanks!

Perhaps I don't fully understand, but I think the answer is: neither :slight_smile:
What I do is output the module as bitcode with the module summary index
added (`llvm::WriteBitcodeToFile`, summary index created with
`llvm::ModuleSummaryIndexBuilder`). This is then passed to the system
linker. The problems arise with ld.gold + LLVMgold plugin. I am using LLVM
3.9.0.

Oh sorry, misunderstood and thought you were implementing in a new linker. For gold you can pass -Wl,-plugin-opt,save-temps and look at the bitcode after each phase of ThinLTO, e.g. I think the files will have .3.import.bc extensions.

Teresa

With save-temps as plugin option, I get extra files for the MAIN module (called a.o): a.o.opt.bc and a.thinlto.bc.
The a.thinlto.bc file contains nothing, only source_filename = ... .
The a.o.opt.bc (this looks like the result after ThinLTO importing and optimization) contains the assembly block that it should not have:


module asm "\09.text"
module asm "\09.globl\09foo"
module asm "\09.align\0916, 0x90"
module asm "\09.type\09foo,@function"
module asm "foo:"
module asm "\09movq %rdi, %rax"
module asm "\09rorw $8, %ax"
module asm "\09ret "
module asm "\09.size\09foo, .-foo"
module asm ""

The asm is the same as in the other module (where it should be defined), and the linkage has not been changed.

Thanks for the help,
Johan

With save-temps as plugin option, I get extra files for the MAIN module (called a.o): a.o.opt.bc and a.thinlto.bc.

What revision is your gold plugin built from? I would expect more temp files.

The a.thinlto.bc file contains nothing, only source_filename = ... .

This is a dump of the combined index. llvm-dis won’t show that, so that’s not unexpected.

The a.o.opt.bc (this looks like the result after ThinLTO importing and optimization) contains the assembly block that it should not have:


module asm "\09.text"
module asm "\09.globl\09foo"
module asm "\09.align\0916, 0x90"
module asm "\09.type\09foo,@function"
module asm "foo:"
module asm "\09movq %rdi, %rax"
module asm "\09rorw $8, %ax"
module asm "\09ret "
module asm "\09.size\09foo, .-foo"
module asm ""

The asm is the same as in the other module (where it should be defined), and the linkage has not been changed.

Will try to reproduce later this morning when I’m back in front of my computer.

Teresa

I’d expect as temps a.thinlto.bc for the index + one file per input. Also there should be a number like: a.o.4.opt.bc

Can you attach the generated temp files?

Thanks,

The plugin version (and LLVM) are LLVM 3.9.0 (the release source tarball).

Archive.zip (10.7 KB)

Ah, you are using 3.9 which explains the lack of additional save-temps files. In any case I just reproduced the multiple def issue with a head compiler. The module assembly is linked into the importing module as you suspected, even though it isn’t referenced. Looks like the IRLinker always appends the module inline assembly when linking in a module (despite the fact that we only link in certain symbols). Will investigate a fix.

Teresa

Filed https://llvm.org/bugs/show_bug.cgi?id=30610 to track, we can continue discussion there.

Thanks,
Teresa