[WebAssembly] Inline asm support.
authorDan Gohman <dan433584@gmail.com>
Fri, 13 Nov 2015 01:42:29 +0000 (01:42 +0000)
committerDan Gohman <dan433584@gmail.com>
Fri, 13 Nov 2015 01:42:29 +0000 (01:42 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@252997 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
lib/Target/WebAssembly/WebAssemblyISelDAGToDAG.cpp
lib/Target/WebAssembly/WebAssemblyISelLowering.cpp
lib/Target/WebAssembly/WebAssemblyISelLowering.h
test/CodeGen/WebAssembly/inline-asm.ll [new file with mode: 0644]

index bcee99578f5d6456e2448239b98817fbf280393f..0ab8888015e2dc3d696a4b2751a131f1ad6b95b2 100644 (file)
@@ -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<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32);
index 58a23756bdc1a197ad4c6098d961dab362ca0bf2..8a02c910ba1e88dc7fa65fe2a35e4edb85c72c14 100644 (file)
@@ -56,6 +56,9 @@ public:
 
   SDNode *Select(SDNode *Node) override;
 
+  bool SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID,
+                                    std::vector<SDValue> &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<SDValue> &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,
index 899e768a0eb20a55d5532ee8eccbd316c578cd7d..b79cbb18f5708fa31dd3bcc07f8c2ec952e01f60 100644 (file)
@@ -211,6 +211,23 @@ WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const {
   return nullptr;
 }
 
+std::pair<unsigned, const TargetRegisterClass *>
+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.
 //===----------------------------------------------------------------------===//
index 23e0c597a33ed12b23a689c416afec7d859ac7fd..c79ffb29c25285952adca2c87a410c2f27048fe1 100644 (file)
@@ -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<unsigned, const TargetRegisterClass *>
+  getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+                               StringRef Constraint, MVT VT) const override;
 
   SDValue LowerCall(CallLoweringInfo &CLI,
                     SmallVectorImpl<SDValue> &InVals) const override;
diff --git a/test/CodeGen/WebAssembly/inline-asm.ll b/test/CodeGen/WebAssembly/inline-asm.ll
new file mode 100644 (file)
index 0000000..5dcd436
--- /dev/null
@@ -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}