codegen_llvm: check inline assembly constraints with LLVM

LLVM provides a way of checking whether the constraints and the actual
inline assembly make sense. This commit introduces a check before
emitting code for the inline assembly. If LLVM rejects the inline
assembly (or its constraints), then the compiler emits an error E0668
("malformed inline assembly").

Signed-off-by: Levente Kurusa <lkurusa@acm.org>
This commit is contained in:
Levente Kurusa 2018-09-25 20:35:19 +02:00
parent e5c6575801
commit fec86c8352
No known key found for this signature in database
GPG Key ID: 28D6B15E6B491269
6 changed files with 53 additions and 6 deletions

View File

@ -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![];
@ -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);
@ -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>,

View File

@ -737,7 +737,7 @@ pub fn phi(&self, ty: &'ll Type, vals: &[&'ll Value], bbs: &[&'ll BasicBlock]) -
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 }
@ -753,9 +753,17 @@ pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char,
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 == 1 {
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
}
}
}

View File

@ -47,4 +47,26 @@ fn main() {
```
"##,
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):
```
#![feature(asm)]
fn main() {
let rax: u64;
unsafe {
asm!("" :"={rax"(rax));
println!("Accumulator is: {}", rax);
}
}
```
"##,
}

View File

@ -1208,6 +1208,9 @@ pub fn LLVMRustInlineAsm(Ty: &Type,
AlignStack: Bool,
Dialect: AsmDialect)
-> &Value;
pub fn LLVMRustInlineAsmVerify(Ty: &Type,
Constraints: *const c_char)
-> Bool;
pub fn LLVMRustDebugMetadataVersion() -> u32;
pub fn LLVMRustVersionMajor() -> u32;

View File

@ -86,7 +86,10 @@ pub fn codegen_statement(&mut self,
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(..) |

View File

@ -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));
}