diff --git a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index 211358ad66c..ee60c8f3a7a 100644 --- a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -267,12 +267,11 @@ bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, if (AsmVariant != 0) report_fatal_error("There are no defined alternate asm variants"); - if (!ExtraCode) { - // TODO: For now, we just hard-code 0 as the constant offset; teach - // SelectInlineAsmMemoryOperand how to do address mode matching. - OS << "0(" + regToString(MI->getOperand(OpNo)) + ')'; - return false; - } + // The current approach to inline asm is that "r" constraints are expressed + // as local indices, rather than values on the operand stack. This simplifies + // using "r" as it eliminates the need to push and pop the values in a + // particular order, however it also makes it impossible to have an "m" + // constraint. So we don't support it. return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS); } diff --git a/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp b/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp index 41249117ae0..e2edb924d4d 100644 --- a/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp +++ b/lib/Target/WebAssembly/WebAssemblyExplicitLocals.cpp @@ -294,6 +294,17 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { unsigned OldReg = MO.getReg(); + // Inline asm may have a def in the middle of the operands. Our contract + // with inline asm register operands is to provide local indices as + // immediates. + if (MO.isDef()) { + assert(MI.getOpcode() == TargetOpcode::INLINEASM); + unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + MRI.removeRegOperandFromUseList(&MO); + MO = MachineOperand::CreateImm(LocalId); + continue; + } + // If we see a stackified register, prepare to insert subsequent // get_locals before the start of its tree. if (MFI.isVRegStackified(OldReg)) { @@ -301,6 +312,15 @@ bool WebAssemblyExplicitLocals::runOnMachineFunction(MachineFunction &MF) { continue; } + // Our contract with inline asm register operands is to provide local + // indices as immediates. + if (MI.getOpcode() == TargetOpcode::INLINEASM) { + unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); + MRI.removeRegOperandFromUseList(&MO); + MO = MachineOperand::CreateImm(LocalId); + continue; + } + // Insert a get_local. unsigned LocalId = getLocalId(Reg2Local, CurLocal, OldReg); const TargetRegisterClass *RC = MRI.getRegClass(OldReg); diff --git a/test/CodeGen/WebAssembly/inline-asm.ll b/test/CodeGen/WebAssembly/inline-asm.ll index 56576305d9e..760b0ad0de6 100644 --- a/test/CodeGen/WebAssembly/inline-asm.ll +++ b/test/CodeGen/WebAssembly/inline-asm.ll @@ -1,4 +1,4 @@ -; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -disable-wasm-explicit-locals -no-integrated-as | FileCheck %s +; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -no-integrated-as | FileCheck %s ; Test basic inline assembly. Pass -no-integrated-as since these aren't ; actually valid assembly syntax. @@ -10,33 +10,24 @@ target triple = "wasm32-unknown-unknown-wasm" ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .result i32{{$}} ; CHECK-NEXT: #APP{{$}} -; CHECK-NEXT: # $0 = aaa($0){{$}} +; CHECK-NEXT: # 0 = aaa(0){{$}} ; CHECK-NEXT: #NO_APP{{$}} -; CHECK-NEXT: return $0{{$}} +; CHECK-NEXT: get_local $push0=, 0{{$}} +; CHECK-NEXT: return $pop0{{$}} define i32 @foo(i32 %r) { entry: %0 = tail call i32 asm sideeffect "# $0 = aaa($1)", "=r,r"(i32 %r) #0, !srcloc !0 ret i32 %0 } -; CHECK-LABEL: bar: -; CHECK-NEXT: .param i32, i32{{$}} -; CHECK-NEXT: #APP{{$}} -; CHECK-NEXT: # 0($1) = bbb(0($0)){{$}} -; CHECK-NEXT: #NO_APP{{$}} -; CHECK-NEXT: return{{$}} -define void @bar(i32* %r, i32* %s) { -entry: - tail call void asm sideeffect "# $0 = bbb($1)", "=*m,*m"(i32* %s, i32* %r) #0, !srcloc !1 - ret void -} - ; CHECK-LABEL: imm: ; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: .local i32{{$}} ; CHECK-NEXT: #APP{{$}} -; CHECK-NEXT: # $0 = ccc(42){{$}} +; CHECK-NEXT: # 0 = ccc(42){{$}} ; CHECK-NEXT: #NO_APP{{$}} -; CHECK-NEXT: return $0{{$}} +; CHECK-NEXT: get_local $push0=, 0{{$}} +; CHECK-NEXT: return $pop0{{$}} define i32 @imm() { entry: %0 = tail call i32 asm sideeffect "# $0 = ccc($1)", "=r,i"(i32 42) #0, !srcloc !2 @@ -47,9 +38,10 @@ entry: ; CHECK-NEXT: .param i64{{$}} ; CHECK-NEXT: .result i64{{$}} ; CHECK-NEXT: #APP{{$}} -; CHECK-NEXT: # $0 = aaa($0){{$}} +; CHECK-NEXT: # 0 = aaa(0){{$}} ; CHECK-NEXT: #NO_APP{{$}} -; CHECK-NEXT: return $0{{$}} +; CHECK-NEXT: get_local $push0=, 0{{$}} +; CHECK-NEXT: return $pop0{{$}} define i64 @foo_i64(i64 %r) { entry: %0 = tail call i64 asm sideeffect "# $0 = aaa($1)", "=r,r"(i64 %r) #0, !srcloc !0 @@ -57,16 +49,20 @@ entry: } ; CHECK-LABEL: X_i16: -; CHECK: foo $1{{$}} -; CHECK: i32.store16 0($0), $1{{$}} +; CHECK: foo 1{{$}} +; CHECK: get_local $push[[S0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[S1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.store16 0($pop[[S0]]), $pop[[S1]]{{$}} define void @X_i16(i16 * %t) { call void asm sideeffect "foo $0", "=*X,~{dirflag},~{fpsr},~{flags},~{memory}"(i16* %t) ret void } ; CHECK-LABEL: X_ptr: -; CHECK: foo $1{{$}} -; CHECK: i32.store 0($0), $1{{$}} +; CHECK: foo 1{{$}} +; CHECK: get_local $push[[S0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: get_local $push[[S1:[0-9]+]]=, 1{{$}} +; CHECK-NEXT: i32.store 0($pop[[S0]]), $pop[[S1]]{{$}} define void @X_ptr(i16 ** %t) { call void asm sideeffect "foo $0", "=*X,~{dirflag},~{fpsr},~{flags},~{memory}"(i16** %t) ret void @@ -87,6 +83,20 @@ define void @varname() { ret void } +; CHECK-LABEL: r_constraint +; CHECK: i32.const $push[[S0:[0-9]+]]=, 0{{$}} +; CHECK-NEXT: set_local [[L0:[0-9]+]], $pop[[S0]]{{$}} +; CHECK-NEXT: i32.const $push[[S1:[0-9]+]]=, 37{{$}} +; CHECK-NEXT: set_local [[L1:[0-9]+]], $pop[[S1]]{{$}} +; CHECK: foo [[L2:[0-9]+]], 1, [[L0]], [[L1]]{{$}} +; CHECK: get_local $push{{[0-9]+}}=, [[L2]]{{$}} +define hidden i32 @r_constraint(i32 %a, i32 %y) { +entry: + %z = bitcast i32 0 to i32 + %t0 = tail call i32 asm "foo $0, $1, $2, $3", "=r,r,r,r"(i32 %y, i32 %z, i32 37) #0, !srcloc !0 + ret i32 %t0 +} + attributes #0 = { nounwind } !0 = !{i32 47}