Skip to content

codegen_llvm: check inline assembly constraints with LLVM #54568

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

Merged
merged 5 commits into from
Sep 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/librustc_codegen_llvm/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn codegen_inline_asm(
ia: &hir::InlineAsm,
outputs: Vec<PlaceRef<'ll, 'tcx>>,
mut inputs: Vec<&'ll Value>
) {
) -> bool {
let mut ext_constraints = vec![];
let mut output_types = vec![];

Expand Down Expand Up @@ -97,6 +97,10 @@ pub fn codegen_inline_asm(
ia.alignstack,
dialect
);
if r.is_none() {
return false;
}
let r = r.unwrap();

// Again, based on how many outputs we have
let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect);
Expand All @@ -117,6 +121,8 @@ pub fn codegen_inline_asm(
llvm::LLVMSetMetadata(r, kind,
llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1));
}

return true;
}

pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>,
Expand Down
16 changes: 12 additions & 4 deletions src/librustc_codegen_llvm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ impl Builder<'a, 'll, 'tcx> {
pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char,
inputs: &[&'ll Value], output: &'ll Type,
volatile: bool, alignstack: bool,
dia: AsmDialect) -> &'ll Value {
dia: AsmDialect) -> Option<&'ll Value> {
self.count_insn("inlineasm");

let volatile = if volatile { llvm::True }
Expand All @@ -753,9 +753,17 @@ impl Builder<'a, 'll, 'tcx> {
debug!("Asm Output Type: {:?}", output);
let fty = Type::func(&argtys[..], output);
unsafe {
let v = llvm::LLVMRustInlineAsm(
fty, asm, cons, volatile, alignstack, dia);
self.call(v, inputs, None)
// Ask LLVM to verify that the constraints are well-formed.
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons);
debug!("Constraint verification result: {:?}", constraints_ok);
if constraints_ok == llvm::True {
let v = llvm::LLVMRustInlineAsm(
fty, asm, cons, volatile, alignstack, dia);
Some(self.call(v, inputs, None))
} else {
// LLVM has detected an issue with our constaints, bail out
None
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/librustc_codegen_llvm/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,26 @@ unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok!
```
"##,

E0668: r##"
Malformed inline assembly rejected by LLVM.

LLVM checks the validity of the constraints and the assembly string passed to
it. This error implies that LLVM seems something wrong with the inline
assembly call.

In particular, it can happen if you forgot the closing bracket of a register
constraint (see issue #51430):
```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail)
#![feature(asm)]

fn main() {
let rax: u64;
unsafe {
asm!("" :"={rax"(rax));
println!("Accumulator is: {}", rax);
}
}
```
"##,

}
3 changes: 3 additions & 0 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,6 +1208,9 @@ extern "C" {
AlignStack: Bool,
Dialect: AsmDialect)
-> &Value;
pub fn LLVMRustInlineAsmVerify(Ty: &Type,
Constraints: *const c_char)
-> Bool;

pub fn LLVMRustDebugMetadataVersion() -> u32;
pub fn LLVMRustVersionMajor() -> u32;
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_codegen_llvm/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ impl FunctionCx<'a, 'll, 'tcx> {
self.codegen_operand(&bx, input).immediate()
}).collect();

asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
let res = asm::codegen_inline_asm(&bx, asm, outputs, input_vals);
if !res {
span_err!(bx.sess(), statement.source_info.span, E0668,
"malformed inline assembly");
}
bx
}
mir::StatementKind::FakeRead(..) |
Expand Down
5 changes: 5 additions & 0 deletions src/rustllvm/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ extern "C" LLVMValueRef LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString,
HasSideEffects, IsAlignStack, fromRust(Dialect)));
}

extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty,
char *Constraints) {
return InlineAsm::Verify(unwrap<FunctionType>(Ty), Constraints);
}

extern "C" void LLVMRustAppendModuleInlineAsm(LLVMModuleRef M, const char *Asm) {
unwrap(M)->appendModuleInlineAsm(StringRef(Asm));
}
Expand Down
47 changes: 47 additions & 0 deletions src/test/ui/inline-asm-bad-constraint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that the compiler will catch invalid inline assembly constraints.

#![feature(asm)]

extern "C" {
fn foo(a: usize);
}

fn main() {
bad_register_constraint();
bad_input();
wrong_size_output();
}

// Issue #54130
fn bad_register_constraint() {
let rax: u64;
unsafe {
asm!("" :"={rax"(rax)) //~ ERROR E0668
};
println!("Accumulator is: {}", rax);
}

// Issue #54376
fn bad_input() {
unsafe {
asm!("callq $0" : : "0"(foo)) //~ ERROR E0668
};
}

fn wrong_size_output() {
let rax: u64 = 0;
unsafe {
asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668
}
println!("rax: {}", rax);
}
21 changes: 21 additions & 0 deletions src/test/ui/inline-asm-bad-constraint.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0668]: malformed inline assembly
--> $DIR/inline-asm-bad-constraint.rs:29:9
|
LL | asm!("" :"={rax"(rax)) //~ ERROR E0668
| ^^^^^^^^^^^^^^^^^^^^^^

error[E0668]: malformed inline assembly
--> $DIR/inline-asm-bad-constraint.rs:37:9
|
LL | asm!("callq $0" : : "0"(foo)) //~ ERROR E0668
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0668]: malformed inline assembly
--> $DIR/inline-asm-bad-constraint.rs:44:9
|
LL | asm!("addb $1, $0" : "={rax}"((0i32, rax))); //~ ERROR E0668
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0668`.