From 3ba85f0622c41d4176922be6b0962352fcc2b79c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 13 Nov 2015 01:42:29 +0000 Subject: [PATCH] [WebAssembly] Inline asm support. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252997 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp | 41 +++++++++++++++++ lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp | 19 ++++++++ lib/Target/WebAssembly/WebAssemblyISelLowering.cpp | 17 +++++++ lib/Target/WebAssembly/WebAssemblyISelLowering.h | 3 ++ test/CodeGen/WebAssembly/inline-asm.ll | 53 ++++++++++++++++++++++ 5 files changed, 133 insertions(+) create mode 100644 test/CodeGen/WebAssembly/inline-asm.ll diff --git a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index bcee99578f5..0ab8888015e 100644 --- a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -77,6 +77,12 @@ private: void EmitFunctionBodyStart() override; void EmitInstruction(const MachineInstr *MI) override; void EmitEndOfAsmFile(Module &M) override; + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &OS) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &OS) override; std::string getRegTypeName(unsigned RegNo) const; const char *toString(MVT VT) const; @@ -275,6 +281,41 @@ void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { OutStreamer->EmitRawText(Text.substr(0, Text.size() - 1)); } +bool WebAssemblyAsmPrinter::PrintAsmOperand(const MachineInstr *MI, + unsigned OpNo, unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &OS) { + if (AsmVariant != 0) + report_fatal_error("There are no defined alternate asm variants"); + + if (!ExtraCode) { + const MachineOperand &MO = MI->getOperand(OpNo); + if (MO.isImm()) + OS << MO.getImm(); + else + OS << regToString(MO); + return false; + } + + return AsmPrinter::PrintAsmOperand(MI, OpNo, AsmVariant, ExtraCode, OS); +} + +bool WebAssemblyAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNo, + unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &OS) { + if (AsmVariant != 0) + report_fatal_error("There are no defined alternate asm variants"); + + if (!ExtraCode) { + OS << regToString(MI->getOperand(OpNo)); + return false; + } + + return AsmPrinter::PrintAsmMemoryOperand(MI, OpNo, AsmVariant, ExtraCode, OS); +} + // Force static initialization. extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { RegisterAsmPrinter X(TheWebAssemblyTarget32); diff --git a/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp b/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp index 58a23756bdc..8a02c910ba1 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp +++ b/lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp @@ -56,6 +56,9 @@ public: SDNode *Select(SDNode *Node) override; + bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) override; + // Include the pieces autogenerated from the target description. #include "WebAssemblyGenDAGISel.inc" @@ -101,6 +104,22 @@ SDNode *WebAssemblyDAGToDAGISel::Select(SDNode *Node) { return ResNode; } +bool WebAssemblyDAGToDAGISel::SelectInlineAsmMemoryOperand( + const SDValue &Op, unsigned ConstraintID, std::vector &OutOps) { + switch (ConstraintID) { + case InlineAsm::Constraint_i: + case InlineAsm::Constraint_m: + // We just support simple memory operands that just have a single address + // operand and need no special handling. + OutOps.push_back(Op); + return false; + default: + break; + } + + return true; +} + /// This pass converts a legalized DAG into a WebAssembly-specific DAG, ready /// for instruction scheduling. FunctionPass *llvm::createWebAssemblyISelDag(WebAssemblyTargetMachine &TM, diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 899e768a0eb..b79cbb18f57 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -211,6 +211,23 @@ WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const { return nullptr; } +std::pair +WebAssemblyTargetLowering::getRegForInlineAsmConstraint( + const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { + // First, see if this is a constraint that directly corresponds to a + // WebAssembly register class. + if (Constraint.size() == 1) { + switch (Constraint[0]) { + case 'r': + return std::make_pair(0U, &WebAssembly::I32RegClass); + default: + break; + } + } + + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + //===----------------------------------------------------------------------===// // WebAssembly Lowering private implementation. //===----------------------------------------------------------------------===// diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/lib/Target/WebAssembly/WebAssemblyISelLowering.h index 23e0c597a33..c79ffb29c25 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -49,6 +49,9 @@ private: bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; MVT getScalarShiftAmountTy(const DataLayout &DL, EVT) const override; const char *getTargetNodeName(unsigned Opcode) const override; + std::pair + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; SDValue LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const override; diff --git a/test/CodeGen/WebAssembly/inline-asm.ll b/test/CodeGen/WebAssembly/inline-asm.ll new file mode 100644 index 00000000000..5dcd436916c --- /dev/null +++ b/test/CodeGen/WebAssembly/inline-asm.ll @@ -0,0 +1,53 @@ +; RUN: llc < %s -asm-verbose=false | FileCheck %s + +; Test basic inline assembly. + +target datalayout = "e-p:32:32-i64:64-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +; CHECK-LABEL: foo: +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .result i32{{$}} +; CHECK-NEXT: .local i32, i32{{$}} +; CHECK-NEXT: #APP{{$}} +; CHECK-NEXT: 1 = aaa(0){{$}} +; CHECK-NEXT: #NO_APP{{$}} +; CHECK-NEXT: return (get_local 1){{$}} +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{{$}} +; CHECK-NEXT: .param i32{{$}} +; CHECK-NEXT: .local i32, i32{{$}} +; CHECK-NEXT: #APP{{$}} +; CHECK-NEXT: 1 = bbb(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: #NO_APP{{$}} +; CHECK-NEXT: return (get_local 0){{$}} +define i32 @imm() { +entry: + %0 = tail call i32 asm sideeffect "$0 = ccc($1)", "=r,i"(i32 42) #0, !srcloc !2 + ret i32 %0 +} + +attributes #0 = { nounwind } + +!0 = !{i32 47} +!1 = !{i32 145} +!2 = !{i32 231} -- 2.11.0