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;
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);
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"
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,
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.
//===----------------------------------------------------------------------===//
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;
--- /dev/null
+; 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}