Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the PNaCl/JS targets to the backend. #28355

Merged
merged 1 commit into from
Oct 22, 2015

Conversation

DiamondLovesYou
Copy link
Contributor

@alexcrichton
Copy link
Member

How close does this get the backend to working? Is this all that's needed or are there still portions in the pipeline? The previous patches up until now have been incremental in the sense that they were just general refactorings we'd want to do anyway, but I'd prefer to have "pnacl not working" to "pnacl working" support land all at once rather than piecemeal.

@DiamondLovesYou
Copy link
Contributor Author

As far as Rust trans is concerned, this gets PNaCl working. In reality, pnacl-llvm needs at least two more passes to handle:

  • LLVM's auto vectorizer (PNaCl currently disables this because it will generate illegal (in the PNaCl ABI sense) vector lengths).
  • Illegal vector types (ie not 128bit width (and not [fiu]64))
  • [fiu]64 vector element types.

Previously, I just disabled the auto vectorizor and added wrappers for the [fiu]64 SIMD types in core, but because of the recent SIMD rfcs, it seems I'll have to write a legalization pass anyway. The main vector legalization pass is currently WIP. The second pass to expand [fiu]64 will happen after.

Also note I'd also like to allow users to disable the setjmp/longjmp EH (which isn't free) in the future (via an unstable -C target-options="blah" argument).

write_output_file(cgcx.handler, tm, cpm, llmod, &path, llvm::ObjectFileType);
});
if !cgcx.is_like_pnacl {
with_codegen(tm, llmod, config.no_builtins, |cpm| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this logic go into pnacl itself? Basically when you request an object file to be emitted from pnacl it seems like it should naturally understand that it instead just emits bitcode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except it's LLVM's target machine that does that. PNaCl/JS (well JS does have one, but it isn't used till post-linking) doesn't have a target machine (though PNaCl does use armv7-none-nacl-gnu for optimizing, using it for codegen would produce an ARM NaCl object file, which isn't compatible with PNaCl).

In fact if you take a look at the toolchain's pnacl-clang, you'll find it's just a python wrapper which invokes clang with arguments like -emit-llvm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may just be me being ignorant of the LLVM fork for pnacl, but if you're already forking LLVM couldn't support for this be added? In terms of interoperating with other compilers it seems like it'd be best to put logic into the pnacl target rather than every compiler using LLVM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But there doesn't exist any PNaCl LLVM target machine in (pnacl-)LLVM's target registry (ie using the PNaCl LLVM fork doesn't help in any way). So there's no LLVM target to tell LLVM how to write an object file (of any format).

In an ideal world, I'd completely throw out the PNaCl IR simplification passes and restart afresh, and I'd do it with an actual, real LLVM target machine (since LLVM's ISD form and PNaCl's IR are actually pretty similar). Sadly, while LLVM's SelectionDAG does most (if not all) the heavy lifting w.r.t. legalizing IR to a specific target, the rest of the toolchain would still need modifications to know how to handle the new PNaCl object format (ie how to read/link), which I would have to create specifically for the target machine (another possible, but still non-trivial thing to implement). The required toolchain modifications are the reason JS also uses bitcode as its object format, despite having an official target machine in LLVM. Plus the translation from the object format back into LLVM IR for codegenning to the real native target.

While I grant that using bitcode as the object format is annoying at times, it is a lot easier because it integrates the preexisting LLVM gold plugin based linker.

I actually agree with you, but the impl cost of what you propose is pretty high vs what emcc and pnacl-clang currently do (ie -emit-llvm (both are just scripts wrapping the real clang underneath; no such luck for Rust)). Plus, the other toolchains would require breaking changes to conform (ie this logic makes Rust conform with the other toolchains).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sounds like we may want to take a similar strategy to those tools and instead of having the default output type be an object file for insertion into an archive we just have the default output type be bitcode to insert into an archive?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--emit=obj would still be broken. Plus the PNaCl linker expects bitcode as the main linker input (ie main.o). This change does accomplish exactly what you propose and doesn't require special casing archive object output.

@alexcrichton
Copy link
Member

@brson, what are your feelings on this? This looks to be one of the only points of divergence where "if pnacl { ... } else { ... }" is needed, and while it's good that it's contained this basically all still looks to me like portions which would be best handled in LLVM somewhere instead of rustc itself (e.g. as it does for all other existing targets). From what @DiamondLovesYou is saying, however, it sounds like the way pnacl is integrated into LLVM makes this not easy unfortunately.

I continue to be worried about the lack of automation we have for this kind of functionality where "some subset" of intrinsics isn't defined on pnacl, but any future intrinsics we add may or may not be part of this subset and we have no way of verifying one way or another.

@brson
Copy link
Contributor

brson commented Sep 21, 2015

These patches are much easier to digest.

I still don't understand why PNaCl believes it's ok not to handle intrinsic that the LLVM supports. The explanation that pnacl is static and can't do it isn't convincing - LLVM itself lowers intrinsics to libm calls and PNaCl can do that too. What happens if we do pass these intrinsic calls to PNaCl? Are they on a blacklist that causes it to fail to compile? Why not just pass the inttrinsic through to libm like LLVM?

If we must do our own intrinsic lowering, then let's do it in a generic way - give target specs a mapping from intrinsics to C calls, not have a pnacl special case. Likewise the is_like_pnacl check for emitting llvm bitcode (into a .o file?) should be a generic 'output_bitcode_to_object_file' or something.

@brson
Copy link
Contributor

brson commented Sep 21, 2015

I guess the issue is that there are no subsequent linkage steps in the pnacl toolchain. The bitcode output by rustc is the final product and there's no opportunity to link libm after the fact.

Edit: but most of these functions are in libm and/or libc, and needed for most software to work. Doesn't PNaCl just always link to libc/libm? (Emscripten supposedly does).

@DiamondLovesYou
Copy link
Contributor Author

@brson Sorry for the delay.

but most of these functions are in libm and/or libc, and needed for most software to work. Doesn't PNaCl just always link to libc/libm? (Emscripten supposedly does).

Sure, since libm is included in libc, but that doesn't mean the linker will actually include the definitions (unless the defs are used), thus their availability isn't guaranteed post link.

Also, PNaCl (and thus Emscripten) disables -simplify-libcalls, so calls to libm won't result in calls to an intrinsic. Thus normal, defined calls to libm (those that would happen in C) will still call libm after optimizations.

If we must do our own intrinsic lowering, then let's do it in a generic way - give target specs a mapping from intrinsics to C calls, not have a pnacl special case. Likewise the is_like_pnacl check for emitting llvm bitcode (into a .o file?) should be a generic 'output_bitcode_to_object_file' or something.

I'm fine with that, if that's what it takes.

@alexcrichton
Copy link
Member

Sure, since libm is included in libc, but that doesn't mean the linker will actually include the definitions (unless the defs are used), thus their availability isn't guaranteed post link.

So if I understand things correctly, there's one giant LLVM module which contains the bitcode for libc, libm, and the application in question. This module is then optimized and such and is passed through the pnacl backend. The definitions of libm functions are all optimized away because they're not used, and then after this happens the intrinsics are lowered to calls to those libm functions, resulting in the undefined symbols?

Is that what's going on?

@kripken
Copy link

kripken commented Sep 28, 2015

Also, PNaCl (and thus Emscripten) disables -simplify-libcalls

I wasn't aware that PNaCl disables that, and I don't think we use code from PNaCl that would cause Emscripten to do it. But maybe I'm wrong - do you know where PNaCl disables that pass?

LLVM source code seems to show that the entire pass has been removed in upstream anyhow?

http://llvm.org/viewvc/llvm-project?view=revision&revision=184459

Unless PNaCl has modified the places where parts of that pass have been moved to, as mentioned in that commit?

@DiamondLovesYou
Copy link
Contributor Author

@alexcrichton

So if I understand things correctly, there's one giant LLVM module which contains the bitcode for libc, libm, and the application in question. This module is then optimized and such and is passed through the pnacl backend. The definitions of libm functions are all optimized away because they're not used, and then after this happens the intrinsics are lowered to calls to those libm functions, resulting in the undefined symbols?

Is that what's going on?

No, the problem is that LLVM's math intrinsics create implicit dependencies on libm, but the linker isn't aware of this fact, and as a result omits libm (or rather, omits the objects containing no defined undefined symbols) from the resulting module. Then, post-link, when the PNaCl IR passes and optimizations are run, llvm.*.* don't have something that can be used in their place.

Normally this isn't an issue because codegen will rewrite these to libm as needed, thus allowing the linker to know about references to them. However, PNaCl/JS use bitcode linking, which doesn't have calls to llvm.*.* rewritten to libm.

I apologize if I'm making this confusing, as this area seems to have recurring misconceptions.

Thankfully, I think my first proposal isn't as bad as I had initially feared (I'm dumb): generally, the implicit libm dep should just be made explicit, because this would also be an issue for any other target if libc is statically linked for WPO (in the same way it's an issue for PNaCl). After that's fixed, creating a PNaCl IR pass to rewritten the math intrinsics won't be an issue. Once I get time, I'll send a patch to upstream LLVM.

@kripken No the file was just moved to a different folder in that commit. See: https://github.com/llvm-mirror/llvm/blob/master/lib/Transforms/Utils/SimplifyLibCalls.cpp

It probably should be disabled, though it probably only creates issues in extreme corner cases. See this comment from pnacl-opt.py from the NaCl SDK:

  # We disable the LLVM simplify-libcalls pass by default, since we
  # statically link in libc, and pnacl-opt is typically used for post-link
  # optimizations.  Changing one library call to another can lead
  # to undefined symbol errors since which definitions from libc are linked
  # in is already decided.

@kripken
Copy link

kripken commented Oct 1, 2015

@DiamondLovesYou : thanks, interesting. I looked into this for emscripten, and it looks like it already isn't run during LTO anyhow (and it's fine to run otherwise). We mostly just do -std-link-opts for LTO, and those don't run simplify-libcalls, perhaps for this very reason. I guess PNaCl runs additional optimizations than -std-link-opts during LTO/their final build stage?

@DiamondLovesYou
Copy link
Contributor Author

@kripken Yeah, PNaCl runs -On in addition to LTO (for even better optimization opportunities).

W.r.t. the intrinsic/libm thingy: after doing some digging in LLVM, it seems LLVM already ensures libm is available at codegen time by emitting references to libm functions. BUT this is only done if there's a target machine available. Le sigh. I may end up creating a dummy target machine and lowering etc for PNaCl.

@DiamondLovesYou
Copy link
Contributor Author

@kripken In case you're interested: I've created a proposal for a PNaCl target machine here based on the discussion in this PR.

@bors
Copy link
Contributor

bors commented Oct 2, 2015

☔ The latest upstream changes (presumably #28768) made this pull request unmergeable. Please resolve the merge conflicts.

@kripken
Copy link

kripken commented Oct 2, 2015

@DiamondLovesYou : thanks for the info.

@alexcrichton
Copy link
Member

@DiamondLovesYou your proposal on the pnacl mailing seems promising! Perhaps this should hold off for a resolution on that?

@DiamondLovesYou
Copy link
Contributor Author

@alexcrichton Sorry, didn't see your reply in my inbox!

There's no need to wait because the requisite changes for pnacl-llvm can/must be done independently of Rust anyway. Not that this PR in its current form should be accepted, merge conflicts notwithstanding. I'll try to make time this weekend to remove the intrinsic and bitcode output logics as well merge with master.

@kripken FYI, I've sent in a patch for the target machine here: https://codereview.chromium.org/1395453003

@DiamondLovesYou DiamondLovesYou force-pushed the pnacl-librustc-trans branch 2 times, most recently from 4470b17 to 1091e72 Compare October 19, 2015 01:21
@DiamondLovesYou
Copy link
Contributor Author

Okay, I've cleaned/removed up the last of the extra logics. This should be ready to merge!

@@ -838,7 +838,9 @@ fn declare_intrinsic(ccx: &CrateContext, key: &str) -> Option<ValueRef> {

ifn!("llvm.trap", fn() -> void);
ifn!("llvm.debugtrap", fn() -> void);
ifn!("llvm.frameaddress", fn(t_i32) -> i8p);
if !ccx.sess().target.target.options.is_like_pnacl {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is the only use case of is_like_pnacl, and it also looks like llvm.frameaddress isn't used at all, so perhaps this could just be removed entirely?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine by me; removed.

@alexcrichton
Copy link
Member

@bors: r+ e497d4a

Thanks for the patience @DiamondLovesYou!

@bors
Copy link
Contributor

bors commented Oct 22, 2015

⌛ Testing commit e497d4a with merge e7b2052...

@bors bors merged commit e497d4a into rust-lang:master Oct 22, 2015
@DiamondLovesYou
Copy link
Contributor Author

The first vector type legalizer pass PR is sent: https://codereview.chromium.org/1423873002.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants