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

Allow unused arguments in asm! #73056

Closed
wants to merge 4 commits into from
Closed

Conversation

Amanieu
Copy link
Member

@Amanieu Amanieu commented Jun 6, 2020

This PR changes the "unused arguments" error for asm! to a warn-by-default lint.

Fixes #72965

@rust-highfive
Copy link
Collaborator

r? @petrochenkov

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 6, 2020
@petrochenkov
Copy link
Contributor

I don't understand the motivation.
Why does in(reg) &dummy in #72965 need to be in the arguments at all if it is not used?

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 6, 2020
@Amanieu
Copy link
Member Author

Amanieu commented Jun 6, 2020

The only use case that I know of is black_box-like functions which "block" compiler optimizations:

#![feature(asm)]

pub fn black_box<T>(mut dummy: T) -> T {
    unsafe {
        asm!("", in(reg) &mut dummy, options(nostack, preserves_flags));
        dummy
    }
}

@petrochenkov
Copy link
Contributor

@Amanieu
What I'm asking is why dummy needs to be passed as an argument to asm! in the black_box example?

In other words, what it the difference between the example above and

pub fn black_box<T>(dummy: T) -> T {
    unsafe {
        asm!("", options(nostack, preserves_flags));
        dummy
    }
}

?

@Amanieu
Copy link
Member Author

Amanieu commented Jun 6, 2020

(actually there's a slight mistake in my code, it should be &mut dummy)

The point of black_box is to effectively suppress the compiler's constant propagation and dead code elimination by making the dummy value look like it is accessed by some external agent.

@petrochenkov
Copy link
Contributor

Looks like I misinterpreted ignored_args in the implementation.
It's only used for diagnostics, so I assumed that the _ args are simply dropped during expansion and don't go anywhere, to LLVM in particular, so it cannot make them look as accessed by an external agent.

@Amanieu
Copy link
Member Author

Amanieu commented Jun 6, 2020

I'm happy to update the code if you can suggest a better name!

@Amanieu
Copy link
Member Author

Amanieu commented Jun 6, 2020

Also, an alternative to this PR would be to turn the unused argument error into a warning that can be suppressed with #[allow].

@petrochenkov petrochenkov added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 6, 2020
@petrochenkov
Copy link
Contributor

petrochenkov commented Jun 7, 2020

By the way, does inline assembly support comments?

If it does, then

pub fn black_box<T>(mut dummy: T) -> T {
    unsafe {
        asm!("/* make it look like {} is accessed by an external agent */",
             in(reg) &mut dummy, options(nostack, preserves_flags));
        dummy
    }
}

will make it look used for rustc.
So we'll need to only add a "use it in an asm comment if necessary" note to the unused argument error.

UPD: Yep, it works at least for x86 asm.

@Amanieu
Copy link
Member Author

Amanieu commented Jun 7, 2020

It does but different architectures use different symbols for comments:

  • X86 uses #
  • ARM uses ; or @
  • AArch64 uses //

This makes it a bit of a mess to write a portable version of black_box. I feel that changing the unused operand error to a lint is a better approach.

@petrochenkov
Copy link
Contributor

@Amanieu
LLVM assembler supports C-style comments, and standalone assemblers that I'm aware of support whole C-style preprocessor in addition to that, so /* comment */ should be pretty portable.

So, I'd personally prefer not extend the syntax util a case for which asm!("/* {} */", arg); doesn't work is discovered in practice.

@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 7, 2020
@Amanieu
Copy link
Member Author

Amanieu commented Jun 7, 2020

I've reverted the syntax changes. I went with the other approach to simply change unused arguments from a hard error to a warn-by-default lint. I feel that we should do this even though you can work around it with comments since the comment hack isn't very discoverable.

@cesarb
Copy link
Contributor

cesarb commented Jun 8, 2020

So, I'd personally prefer not extend the syntax util a case for which asm!("/* {} */", arg); doesn't work is discovered in practice.

You cannot guarantee that a future assembly syntax for a future ISA will not use /* for a non-comment purpose. That is, if you depend on comments to fool the "unused arguments" error, you need a comment syntax that is guaranteed to be the same for every current or future ISA.

That said, black_box and similar are "special": any other use of inline assembly will have actual assembly code, and therefore be specific to a single ISA or ISA family, so the writer of that inline assembly will know the correct comment syntax. Therefore, another option would be to add a compiler intrinsic for black_box, but that compiler intrinsic would have to be flexible enough for all optimization barrier use cases.

There are at least two main cases that I can think of: register value (asm!("", inlateout(reg) value, options(pure, nomem, nostack, preserves_flags)), which could be used for instance for the constant_time_eq crate) and memory reference (asm!("", in(reg) &mut value, options(nostack, preserves_flags))), but there might be others (the clear_on_drop crate, for instance, has one variant for sized and one variant for unsized values, though they would be the same with the new asm! since it doesn't have memory operands yet). With memory operands, one could have a third main case which is a hybrid of the other two, something like asm!("", inout(mem) &mut value, options(pure, nostack, preserves_flags)).

So I think that the simplest and most flexible approach is to just allow arguments to be missing from the template string, but still be treated as used by the compiler, either through a lint which can be allowed, or through a special named argument or some other syntax trickery. There is a lot of precedent for that (not only the Rust black_box function, but also the Linux kernel's RELOC_HIDE, OPTIMIZER_HIDE_VAR, and barrier/barrier_data macros).

@petrochenkov
Copy link
Contributor

Hmm, this raises some compatibility questions regarding C-style comments in asm!.

Looks like /**/ comments are processed in a generic way by LLVM infrastructure (see llvm-project\llvm\lib\MC\MCParser\Asm(Lexer,Parser).cpp), not by specific implementations of asm parsers in llvm-project\llvm\lib\Target.

It means that

  • no specific LLVM target assembler can use C-style comments for non-comment purposes
  • if an alternative backend like cranelift starts supporting asm! it will either have to do the same thing, or feed /**/ to an external assembler which is not guaranteed to support it.

This is a more general issue, not specific to unused arguments.

@Amanieu
Copy link
Member Author

Amanieu commented Jun 8, 2020

Fixed the lint name and squashed.

@rust-highfive

This comment has been minimized.

@nagisa
Copy link
Member

nagisa commented Jun 8, 2020

Lint makes sense to me, but why warn-by-default? Users who end up needing to have unused arguments in their code will want to #[allow] the lint either way, so wouldn’t it be better to just make UX better to everybody else by continuing to error?

(Furthermore decreasing the lint level is something that cannot be undone later – but we can always downgrade the default severity later if we think we need to still)

@Amanieu
Copy link
Member Author

Amanieu commented Jun 8, 2020

Most of our deny-by-default lints are for future-incompat issues or things that will panic at runtime (e.g. overflow). It felt more consistent to make it warn by default like all the other unused X lints.

@petrochenkov petrochenkov added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jun 8, 2020
@petrochenkov petrochenkov added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 8, 2020
@petrochenkov petrochenkov added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 9, 2020
@Amanieu
Copy link
Member Author

Amanieu commented Jun 10, 2020

I somewhat got it working by moving the unused argument check to the AST lowering pass where the node ID of the asm! is available. However I can't get #[allow] to work when directly applied to the asm! statement:

// Works
#[allow(unused_asm_arguments)]
{
    asm!("", in(reg) 0, in(reg) 1);
}

// Doesn't work
#[allow(unused_asm_arguments)]
asm!("", in(reg) 0, in(reg) 1);

@rust-highfive
Copy link
Collaborator

The job x86_64-gnu-llvm-8 of your PR failed (pretty log, raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
##[section]Starting: Linux x86_64-gnu-llvm-8
##[section]Starting: Initialize job
Agent name: 'Azure Pipelines 2'
Agent machine name: 'fv-az578'
Current agent version: '2.170.1'
##[group]Operating System
16.04.6
LTS
LTS
##[endgroup]
##[group]Virtual Environment
Environment: ubuntu-16.04
Version: 20200517.1
Included Software: https://github.com/actions/virtual-environments/blob/ubuntu16/20200517.1/images/linux/Ubuntu1604-README.md
##[endgroup]
Agent running as: 'vsts'
Prepare build directory.
Set build variables.
Download all required tasks.
Download all required tasks.
Downloading task: Bash (3.163.3)
Checking job knob settings.
   Knob: AgentToolsDirectory = /opt/hostedtoolcache Source: ${AGENT_TOOLSDIRECTORY} 
   Knob: AgentPerflog = /home/vsts/perflog Source: ${VSTS_AGENT_PERFLOG} 
Start tracking orphan processes.
##[section]Finishing: Initialize job
##[section]Starting: Configure Job Name
==============================================================================
---
========================== Starting Command Output ===========================
[command]/bin/bash --noprofile --norc /home/vsts/work/_temp/fee10d0b-fec4-4537-b9c2-114fcb07df4f.sh

##[section]Finishing: Disable git automatic line ending conversion
##[section]Starting: Checkout rust-lang/rust@refs/pull/73056/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
---
##[command]git remote add origin https://github.com/rust-lang/rust
##[command]git config gc.auto 0
##[command]git config --get-all http.https://github.com/rust-lang/rust.extraheader
##[command]git config --get-all http.proxy
##[command]git -c http.extraheader="AUTHORIZATION: basic ***" fetch --force --tags --prune --progress --no-recurse-submodules --depth=2 origin +refs/heads/*:refs/remotes/origin/* +refs/pull/73056/merge:refs/remotes/pull/73056/merge
---
 ---> cb2676f08729
Step 5/8 : ENV RUST_CONFIGURE_ARGS       --build=x86_64-unknown-linux-gnu       --llvm-root=/usr/lib/llvm-8       --enable-llvm-link-shared       --set rust.thin-lto-import-instr-limit=10
 ---> Using cache
 ---> df25ce111862
Step 6/8 : ENV SCRIPT python2.7 ../x.py test --exclude src/tools/tidy &&            python2.7 ../x.py test src/test/mir-opt --pass=build                                   --target=armv5te-unknown-linux-gnueabi &&            python2.7 ../x.py test src/tools/tidy
 ---> 599b9ac96b27
Step 7/8 : ENV NO_DEBUG_ASSERTIONS=1
 ---> Using cache
 ---> 091087e35a36
---
   Compiling rustc_parse_format v0.0.0 (/checkout/src/librustc_parse_format)
   Compiling chalk-rust-ir v0.10.0
   Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
   Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
   Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
   Compiling chalk-solve v0.10.0
   Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
   Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
   Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
   Compiling rustc_parse_format v0.0.0 (/checkout/src/librustc_parse_format)
   Compiling chalk-rust-ir v0.10.0
   Compiling rustc_ast_pretty v0.0.0 (/checkout/src/librustc_ast_pretty)
   Compiling rustc_hir v0.0.0 (/checkout/src/librustc_hir)
   Compiling rustc_query_system v0.0.0 (/checkout/src/librustc_query_system)
   Compiling chalk-solve v0.10.0
   Compiling rustc_hir_pretty v0.0.0 (/checkout/src/librustc_hir_pretty)
   Compiling rustc_parse v0.0.0 (/checkout/src/librustc_parse)
   Compiling rustc_ast_lowering v0.0.0 (/checkout/src/librustc_ast_lowering)
---
Check compiletest suite=ui mode=ui (x86_64-unknown-linux-gnu -> x86_64-unknown-linux-gnu)

running 10292 tests
.................................................................................................... 100/10292
..............................................iF...ii............................................... 200/10292
.................................................................................................... 400/10292
.................................................................................................... 500/10292
..................................................................................................i. 600/10292
.................................................................................................... 700/10292
---
..............................i...............i..................................................... 5200/10292
.................................................................................................... 5300/10292
..............................................................................i..................... 5400/10292
........................................................................i........................... 5500/10292
.........................................................................................ii.ii...... 5600/10292
..i...i............................................................................................. 5700/10292
........................................i........................................................... 5900/10292
..............................................................................................ii.... 6000/10292
.................................i.................................................................. 6100/10292
.................................................................................................... 6200/10292
.................................................................................................... 6200/10292
.................................................................................................... 6300/10292
........................................................ii...i..ii...........i...................... 6400/10292
.................................................................................................... 6600/10292
.................................................................................................... 6700/10292
.................................................................................................... 6700/10292
.........................................................................................i..ii...... 6800/10292
.................................................................................................... 7000/10292
.................................................................................................... 7100/10292
...........................................i........................................................ 7200/10292
.................................................................................................... 7300/10292
---
.................................................................................................... 8200/10292
.................................................................................................... 8300/10292
...................................................................................i................ 8400/10292
.................................................................................................... 8500/10292
.....................................iiiiii.iiiiii.i................................................ 8600/10292
.................................................................................................... 8800/10292
.................................................................................................... 8900/10292
.................................................................................................... 9000/10292
.................................................................................................... 9100/10292
---
failures:

---- [ui] ui/asm/bad-template.rs stdout ----

error: /checkout/src/test/ui/asm/bad-template.rs:28: unexpected warning: '28:18: 28:27: asm arguments not used in template [unused_asm_arguments]'
error: 1 unexpected errors found, 0 expected errors not found
status: exit code: 1
status: exit code: 1
command: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/src/test/ui/asm/bad-template.rs" "-Zthreads=1" "--target=x86_64-unknown-linux-gnu" "--error-format" "json" "-Zui-testing" "-Zdeduplicate-diagnostics=no" "--emit" "metadata" "-C" "prefer-dynamic" "--out-dir" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/asm/bad-template" "-A" "unused" "-Crpath" "-O" "-Cdebuginfo=0" "-Zunstable-options" "-Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "-L" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui/asm/bad-template/auxiliary"
    Error {
        line_num: 28,
        kind: Some(
            Warning,
            Warning,
        ),
        msg: "28:18: 28:27: asm arguments not used in template [unused_asm_arguments]",
]

thread '[ui] ui/asm/bad-template.rs' panicked at 'explicit panic', src/tools/compiletest/src/runtest.rs:1460:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---

thread 'main' panicked at 'Some tests failed', src/tools/compiletest/src/main.rs:348:22


command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0-tools-bin/compiletest" "--compile-lib-path" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/lib" "--run-lib-path" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/lib/rustlib/x86_64-unknown-linux-gnu/lib" "--rustc-path" "/checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" "--src-base" "/checkout/src/test/ui" "--build-base" "/checkout/obj/build/x86_64-unknown-linux-gnu/test/ui" "--stage-id" "stage2-x86_64-unknown-linux-gnu" "--mode" "ui" "--target" "x86_64-unknown-linux-gnu" "--host" "x86_64-unknown-linux-gnu" "--llvm-filecheck" "/usr/lib/llvm-8/bin/FileCheck" "--nodejs" "/usr/bin/node" "--host-rustcflags" "-Crpath -O -Cdebuginfo=0 -Zunstable-options  -Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "--target-rustcflags" "-Crpath -O -Cdebuginfo=0 -Zunstable-options  -Lnative=/checkout/obj/build/x86_64-unknown-linux-gnu/native/rust-test-helpers" "--docck-python" "/usr/bin/python2.7" "--lldb-python" "/usr/bin/python2.7" "--gdb" "/usr/bin/gdb" "--quiet" "--llvm-version" "8.0.0" "--system-llvm" "--cc" "" "--cxx" "" "--cflags" "" "--llvm-components" "" "--adb-path" "adb" "--adb-test-dir" "/data/tmp/work" "--android-cross-path" "" "--color" "always"


failed to run: /checkout/obj/build/bootstrap/debug/bootstrap test --exclude src/tools/tidy
Build completed unsuccessfully in 0:55:49
Build completed unsuccessfully in 0:55:49
== clock drift check ==
  local time: Wed Jun 10 07:50:26 UTC 2020
  network time: Wed, 10 Jun 2020 07:50:26 GMT
== end clock drift check ==

##[error]Bash exited with code '1'.
##[section]Finishing: Run build
##[section]Starting: Checkout rust-lang/rust@refs/pull/73056/merge to s
Task         : Get sources
Description  : Get sources from a repository. Supports Git, TfsVC, and SVN repositories.
Version      : 1.0.0
Author       : Microsoft
Author       : Microsoft
Help         : [More Information](https://go.microsoft.com/fwlink/?LinkId=798199)
==============================================================================
Cleaning any cached credential from repository: rust-lang/rust (GitHub)
##[section]Finishing: Checkout rust-lang/rust@refs/pull/73056/merge to s
Cleaning up task key
Start cleaning up orphan processes.
Terminate orphan process: pid (3343) (python)
##[section]Finishing: Finalize Job

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @rust-lang/infra. (Feature Requests)

@petrochenkov
Copy link
Contributor

It's good that this can be reported after expansion, when all NodeIds are known, the location will be slightly more precise than with #73178 (expression-level vs function-level).

@petrochenkov
Copy link
Contributor

AST lowering is not a good place for that though, it should be possible to add the unused_asm_arguments to early_lint_passes now, then it will be benefit from batching and parallel execution that the lint infra provides.

@petrochenkov
Copy link
Contributor

However I can't get #[allow] to work when directly applied to the asm! statement

That's just how lints and macros work in general - macro calls don't have node ids to which lint levels can be attached (also see #63221).

Given the lint inconveniences and #72016 (comment) perhaps it's time to reconsider

error: argument never used
 --> src/main.rs:5:14
  |
5 |     asm!("", in(reg) a);
  |              ^^^^^^^^^ consider using it in an asm comment: asm!("/*{}*/", in(reg) a)

@Amanieu
Copy link
Member Author

Amanieu commented Jun 10, 2020

However in this case the macro expands to an ExprKind::InlineAsm which does have a NodeId. Shouldn't the #[allow] apply to that node in this case?

@Amanieu
Copy link
Member Author

Amanieu commented Jun 10, 2020

In fact if I wrap the asm! in parentheses it works:

#[allow(unused_asm_arguments)]
(asm!("", in(reg) 0, in(reg) 1));

I think it should be possible to make the #[allow] work when applied to a bare asm!.

@petrochenkov
Copy link
Contributor

petrochenkov commented Jun 10, 2020

That what #63221 is about - the current language behavior is that inert attributes on foo!() disappear during expansion of foo!().

(It may be possible to change it, but it's a big and potentially breaking change.)

EDIT: it may also be possible to change built-in allow/warn/deny attributes specifically - they still disappear, but register themselves in some table available to the lint system before that. Pretty hacky, but may be generally useful.

@Amanieu
Copy link
Member Author

Amanieu commented Jun 11, 2020

Closing in favor of #73230.

@Amanieu Amanieu closed this Jun 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"argument never used" in new asm! syntax
5 participants