From: JF Bastien Date: Mon, 24 Aug 2015 22:16:48 +0000 (+0000) Subject: WebAssembly: Implement call X-Git-Url: http://plrg.eecs.uci.edu/git/?p=oota-llvm.git;a=commitdiff_plain;h=3a9c43563bb0604b6bfcc7d5a44a0331633593ce WebAssembly: Implement call Summary: Support function calls. Reviewers: sunfish, sunfishcode Subscribers: sunfishcode, jfb, llvm-commits Differential revision: http://reviews.llvm.org/D12219 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@245887 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp index 0c5792c6b51..cbd2f7dc748 100644 --- a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -44,3 +44,16 @@ void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS, printInstruction(MI, OS); printAnnotation(OS, Annot); } + +void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) + O << getRegisterName(Op.getReg()); + else if (Op.isImm()) + O << '#' << Op.getImm(); + else { + assert(Op.isExpr() && "unknown operand kind in printOperand"); + Op.getExpr()->print(O, &MAI); + } +} diff --git a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h index 4c54a525577..80fe29d56f7 100644 --- a/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h +++ b/lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h @@ -32,6 +32,9 @@ public: void printInst(const MCInst *MI, raw_ostream &OS, StringRef Annot, const MCSubtargetInfo &STI) override; + // Used by tblegen code. + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O); + // Autogenerated by tblgen. void printInstruction(const MCInst *MI, raw_ostream &O); static const char *getRegisterName(unsigned RegNo); diff --git a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp index a3cbcbbeaf6..065ef87fb35 100644 --- a/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -84,7 +84,10 @@ static SmallString<32> Name(const WebAssemblyInstrInfo *TII, return SmallString<32>(&N[0], &N[End]); } +static std::string toSymbol(StringRef S) { return ("$" + S).str(); } + void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { + DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); SmallString<128> Str; raw_svector_ostream OS(Str); @@ -132,6 +135,9 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { assert(Written < BufBytes); OS << ' ' << buf; } break; + case MachineOperand::MO_GlobalAddress: { + OS << ' ' << toSymbol(MO.getGlobal()->getName()); + } break; } OS << ')'; diff --git a/lib/Target/WebAssembly/WebAssemblyISD.def b/lib/Target/WebAssembly/WebAssemblyISD.def new file mode 100644 index 00000000000..0bc9b2f55b1 --- /dev/null +++ b/lib/Target/WebAssembly/WebAssemblyISD.def @@ -0,0 +1,22 @@ +//- WebAssemblyISD.def - WebAssembly ISD ---------------------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file describes the various WebAssembly ISD node types. +/// +//===----------------------------------------------------------------------===// + +// NOTE: NO INCLUDE GUARD DESIRED! + +HANDLE_NODETYPE(CALL) +HANDLE_NODETYPE(RETURN) +HANDLE_NODETYPE(ARGUMENT) +HANDLE_NODETYPE(Wrapper) + +// add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here... diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp index 98a5b6829b6..c309db48f63 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -19,6 +19,7 @@ #include "WebAssemblyTargetMachine.h" #include "WebAssemblyTargetObjectFile.h" #include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/IR/DiagnosticInfo.h" @@ -92,6 +93,8 @@ int DiagnosticInfoUnsupported::KindID = 0; WebAssemblyTargetLowering::WebAssemblyTargetLowering( const TargetMachine &TM, const WebAssemblySubtarget &STI) : TargetLowering(TM), Subtarget(&STI) { + auto MVTPtr = Subtarget->hasAddr64() ? MVT::i64 : MVT::i32; + // Booleans always contain 0 or 1. setBooleanContents(ZeroOrOneBooleanContent); // WebAssembly does not produce floating-point exceptions on normal floating @@ -112,6 +115,8 @@ WebAssemblyTargetLowering::WebAssemblyTargetLowering( // FIXME: many setOperationAction are missing... + setOperationAction(ISD::GlobalAddress, MVTPtr, Custom); + for (auto T : {MVT::f32, MVT::f64}) { // Don't expand the floating-point types to constant pools. setOperationAction(ISD::ConstantFP, T, Legal); @@ -156,6 +161,13 @@ FastISel *WebAssemblyTargetLowering::createFastISel( return WebAssembly::createFastISel(FuncInfo, LibInfo); } +bool WebAssemblyTargetLowering::isOffsetFoldingLegal( + const GlobalAddressSDNode *GA) const { + // The WebAssembly target doesn't support folding offsets into global + // addresses. + return false; +} + MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout &DL, EVT VT) const { return VT.getSimpleVT(); @@ -164,9 +176,13 @@ MVT WebAssemblyTargetLowering::getScalarShiftAmountTy(const DataLayout &DL, const char * WebAssemblyTargetLowering::getTargetNodeName(unsigned Opcode) const { switch (static_cast(Opcode)) { - case WebAssemblyISD::FIRST_NUMBER: break; - case WebAssemblyISD::RETURN: return "WebAssemblyISD::RETURN"; - case WebAssemblyISD::ARGUMENT: return "WebAssemblyISD::ARGUMENT"; + case WebAssemblyISD::FIRST_NUMBER: + break; +#define HANDLE_NODETYPE(NODE) \ + case WebAssemblyISD::NODE: \ + return "WebAssemblyISD::" #NODE; +#include "WebAssemblyISD.def" +#undef HANDLE_NODETYPE } return nullptr; } @@ -185,7 +201,6 @@ static void fail(SDLoc DL, SelectionDAG &DAG, const char *msg) { DiagnosticInfoUnsupported(DL, *MF.getFunction(), msg, SDValue())); } -<<<<<<< HEAD SDValue WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { @@ -205,7 +220,6 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, SmallVectorImpl &Outs = CLI.Outs; SmallVectorImpl &OutVals = CLI.OutVals; - Type *retTy = CLI.RetTy; bool IsStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet(); if (IsStructRet) fail(DL, DAG, "WebAssembly doesn't support struct return yet"); @@ -213,7 +227,6 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); SmallVectorImpl &Ins = CLI.Ins; - ArgListTy &Args = CLI.getArgs(); bool IsVarArg = CLI.IsVarArg; if (IsVarArg) fail(DL, DAG, "WebAssembly doesn't support varargs yet"); @@ -223,33 +236,35 @@ WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, unsigned NumBytes = CCInfo.getNextStackOffset(); auto PtrVT = getPointerTy(MF.getDataLayout()); - auto Zero = DAG.getConstant(0, CLI.DL, PtrVT, true); - auto NB = DAG.getConstant(NumBytes, CLI.DL, PtrVT, true); - Chain = DAG.getCALLSEQ_START(Chain, NB, CLI.DL); + auto Zero = DAG.getConstant(0, DL, PtrVT, true); + auto NB = DAG.getConstant(NumBytes, DL, PtrVT, true); + Chain = DAG.getCALLSEQ_START(Chain, NB, DL); SmallVector Ops; Ops.push_back(Chain); - Ops.push_back(CLI.Callee); - Ops.append(CLI.OutVals.begin(), CLI.OutVals.end()); + Ops.push_back(Callee); + Ops.append(OutVals.begin(), OutVals.end()); SmallVector Tys; - for (const auto &In : CLI.Ins) + for (const auto &In : Ins) Tys.push_back(In.VT); Tys.push_back(MVT::Other); - SDVTList TyList = CLI.DAG.getVTList(Tys); - SDValue Res = CLI.DAG.getNode(WebAssemblyISD::CALL, CLI.DL, TyList, Ops); - InVals.push_back(Res); - Chain = Res.getValue(1); + SDVTList TyList = DAG.getVTList(Tys); + SDValue Res = DAG.getNode(WebAssemblyISD::CALL, DL, TyList, Ops); + if (Ins.empty()) { + Chain = Res; + } else { + InVals.push_back(Res); + Chain = Res.getValue(1); + } // FIXME: handle CLI.RetSExt and CLI.RetZExt? - Chain = CLI.DAG.getCALLSEQ_END(Chain, NB, Zero, SDValue(), CLI.DL); + Chain = DAG.getCALLSEQ_END(Chain, NB, Zero, SDValue(), DL); return Chain; } -======= ->>>>>>> parent of 03685a9... call bool WebAssemblyTargetLowering::CanLowerReturn( CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, const SmallVectorImpl &Outs, LLVMContext &Context) const { @@ -326,9 +341,34 @@ SDValue WebAssemblyTargetLowering::LowerFormalArguments( } //===----------------------------------------------------------------------===// -// Other Lowering Code +// Custom lowering hooks. //===----------------------------------------------------------------------===// +SDValue WebAssemblyTargetLowering::LowerOperation(SDValue Op, + SelectionDAG &DAG) const { + switch (Op.getOpcode()) { + default: + llvm_unreachable("unimplemented operation lowering"); + return SDValue(); + case ISD::GlobalAddress: + return LowerGlobalAddress(Op, DAG); + } +} + +SDValue WebAssemblyTargetLowering::LowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + const auto *GA = cast(Op); + EVT VT = Op.getValueType(); + assert(GA->getOffset() == 0 && + "offsets on global addresses are forbidden by isOffsetFoldingLegal"); + assert(GA->getTargetFlags() == 0 && "WebAssembly doesn't set target flags"); + if (GA->getAddressSpace() != 0) + fail(DL, DAG, "WebAssembly only expects the 0 address space"); + return DAG.getNode(WebAssemblyISD::Wrapper, DL, VT, + DAG.getTargetGlobalAddress(GA->getGlobal(), DL, VT)); +} + //===----------------------------------------------------------------------===// // WebAssembly Optimization Hooks //===----------------------------------------------------------------------===// diff --git a/lib/Target/WebAssembly/WebAssemblyISelLowering.h b/lib/Target/WebAssembly/WebAssemblyISelLowering.h index ea845cd4031..119d0f5ef43 100644 --- a/lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ b/lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -24,10 +24,9 @@ namespace WebAssemblyISD { enum NodeType : unsigned { FIRST_NUMBER = ISD::BUILTIN_OP_END, - RETURN, - ARGUMENT, - - // add memory opcodes starting at ISD::FIRST_TARGET_MEMORY_OPCODE here... +#define HANDLE_NODETYPE(NODE) NODE, +#include "WebAssemblyISD.def" +#undef HANDLE_NODETYPE }; } // end namespace WebAssemblyISD @@ -47,26 +46,29 @@ private: FastISel *createFastISel(FunctionLoweringInfo &FuncInfo, const TargetLibraryInfo *LibInfo) const override; - + bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; MVT getScalarShiftAmountTy(const DataLayout &DL, EVT) const override; - const char *getTargetNodeName(unsigned Opcode) const override; + SDValue LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, bool isVarArg, const SmallVectorImpl &Outs, LLVMContext &Context) const override; - SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, SDLoc dl, SelectionDAG &DAG) const override; - SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Ins, SDLoc DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const override; + + // Custom lowering hooks. + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; }; namespace WebAssembly { diff --git a/lib/Target/WebAssembly/WebAssemblyInstrCall.td b/lib/Target/WebAssembly/WebAssemblyInstrCall.td index 6b5b6cd5417..abb7f21512a 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrCall.td @@ -12,6 +12,27 @@ /// //===----------------------------------------------------------------------===// +// The call sequence start/end LLVM-isms isn't useful to WebAssembly since it's +// a virtual ISA. +let isCodeGenOnly = 1 in { +def : I<(outs), (ins i64imm:$amt), + [(WebAssemblycallseq_start timm:$amt)]>; +def : I<(outs), (ins i64imm:$amt1, i64imm:$amt2), + [(WebAssemblycallseq_end timm:$amt1, timm:$amt2)]>; +} // isCodeGenOnly = 1 + +multiclass CALL { + def CALL_#vt : I<(outs vt:$dst), (ins Int32:$callee, variable_ops), + [(set vt:$dst, (WebAssemblycall Int32:$callee))]>; +} +let Uses = [SP32, SP64], isCall = 1 in { + defm : CALL; + defm : CALL; + defm : CALL; + defm : CALL; + // FIXME: void. +} // Uses = [SP32,SP64], isCall = 1 + /* * TODO(jfb): Add the following. * diff --git a/lib/Target/WebAssembly/WebAssemblyInstrControl.td b/lib/Target/WebAssembly/WebAssemblyInstrControl.td index 3025e24fb84..5f53e4a00d4 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrControl.td @@ -28,12 +28,10 @@ multiclass RETURN { def RETURN_#vt : I<(outs), (ins vt:$val), [(WebAssemblyreturn vt:$val)]>; } -let hasSideEffects = 1, isReturn = 1, isTerminator = 1, hasCtrlDep = 1, - isBarrier = 1 in { +let isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 in { defm : RETURN; defm : RETURN; defm : RETURN; defm : RETURN; def RETURN_VOID : I<(outs), (ins), [(WebAssemblyreturn)]>; -} // hasSideEffects = 1, isReturn = 1, isTerminator = 1, hasCtrlDep = 1, - // isBarrier = 1 +} // isReturn = 1, isTerminator = 1, hasCtrlDep = 1, isBarrier = 1 diff --git a/lib/Target/WebAssembly/WebAssemblyInstrFormats.td b/lib/Target/WebAssembly/WebAssemblyInstrFormats.td index f4d16d39e64..61de5ddd716 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrFormats.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrFormats.td @@ -12,7 +12,7 @@ /// //===----------------------------------------------------------------------===// -// WebAssembly Instruction Format +// WebAssembly Instruction Format. class WebAssemblyInst : Instruction { field bits<0> Inst; // Instruction encoding. let Namespace = "WebAssembly"; @@ -20,7 +20,7 @@ class WebAssemblyInst : Instruction { let Constraints = cstr; } -// Normal instructions +// Normal instructions. class I pattern, string cstr = ""> : WebAssemblyInst { dag OutOperandList = oops; diff --git a/lib/Target/WebAssembly/WebAssemblyInstrInfo.td b/lib/Target/WebAssembly/WebAssemblyInstrInfo.td index c6335723b4c..d73e66ea985 100644 --- a/lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ b/lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -25,18 +25,34 @@ def HasSIMD128 : Predicate<"Subtarget->hasSIMD128()">, // WebAssembly-specific DAG Node Types. //===----------------------------------------------------------------------===// +def SDT_WebAssemblyCallSeqStart : SDCallSeqStart<[SDTCisVT<0, iPTR>]>; +def SDT_WebAssemblyCallSeqEnd : + SDCallSeqEnd<[SDTCisVT<0, iPTR>, SDTCisVT<1, iPTR>]>; +def SDT_WebAssemblyCall : SDTypeProfile<1, -1, [SDTCisPtrTy<1>]>; def SDT_WebAssemblyArgument : SDTypeProfile<1, 1, [SDTCisVT<1, i32>]>; def SDT_WebAssemblyReturn : SDTypeProfile<0, -1, []>; +def SDT_WebAssemblyWrapper : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>, + SDTCisPtrTy<0>]>; //===----------------------------------------------------------------------===// // WebAssembly-specific DAG Nodes. //===----------------------------------------------------------------------===// +def WebAssemblycallseq_start : + SDNode<"ISD::CALLSEQ_START", SDT_WebAssemblyCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; +def WebAssemblycallseq_end : + SDNode<"ISD::CALLSEQ_END", SDT_WebAssemblyCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; +def WebAssemblycall : SDNode<"WebAssemblyISD::CALL", + SDT_WebAssemblyCall, + [SDNPHasChain, SDNPVariadic]>; def WebAssemblyargument : SDNode<"WebAssemblyISD::ARGUMENT", SDT_WebAssemblyArgument>; def WebAssemblyreturn : SDNode<"WebAssemblyISD::RETURN", - SDT_WebAssemblyReturn, - [SDNPHasChain, SDNPSideEffect]>; + SDT_WebAssemblyReturn, [SDNPHasChain]>; +def WebAssemblywrapper : SDNode<"WebAssemblyISD::Wrapper", + SDT_WebAssemblyWrapper>; //===----------------------------------------------------------------------===// // WebAssembly-specific Operands. @@ -49,6 +65,8 @@ def WebAssemblyreturn : SDNode<"WebAssemblyISD::RETURN", * set_local: set the current value of a local variable */ +def global : Operand; + //===----------------------------------------------------------------------===// // WebAssembly Instruction Format Definitions. //===----------------------------------------------------------------------===// @@ -74,6 +92,10 @@ def Immediate_F32 : I<(outs Float32:$res), (ins f32imm:$imm), def Immediate_F64 : I<(outs Float64:$res), (ins f64imm:$imm), [(set Float64:$res, fpimm:$imm)]>; +// Special types of immediates. +def GLOBAL : I<(outs Int32:$dst), (ins global:$addr), + [(set Int32:$dst, (WebAssemblywrapper tglobaladdr:$addr))]>; + //===----------------------------------------------------------------------===// // Additional sets of instructions. //===----------------------------------------------------------------------===// diff --git a/test/CodeGen/WebAssembly/call.ll b/test/CodeGen/WebAssembly/call.ll index e5d468b3cd1..ae66fae840c 100644 --- a/test/CodeGen/WebAssembly/call.ll +++ b/test/CodeGen/WebAssembly/call.ll @@ -5,18 +5,61 @@ target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128" target triple = "wasm32-unknown-unknown" -declare void @nullary() - -; CHECK-LABEL: call_nullary: -; CHECK-NEXT: (call @foo) -; CHECK-NEXT: (return) -define void @call_nullary() { - call void @nullary() - ret void +declare i32 @i32_nullary() +declare i32 @i32_unary(i32) +declare i64 @i64_nullary() +declare float @float_nullary() +declare double @double_nullary() + +; CHECK-LABEL: call_i32_nullary: +; CHECK-NEXT: (setlocal @0 (global $i32_nullary)) +; CHECK-NEXT: (setlocal @1 (call @0)) +; CHECK-NEXT: (return @1) +define i32 @call_i32_nullary() { + %r = call i32 @i32_nullary() + ret i32 %r +} + +; CHECK-LABEL: call_i64_nullary: +; CHECK-NEXT: (setlocal @0 (global $i64_nullary)) +; CHECK-NEXT: (setlocal @1 (call @0)) +; CHECK-NEXT: (return @1) +define i64 @call_i64_nullary() { + %r = call i64 @i64_nullary() + ret i64 %r +} + +; CHECK-LABEL: call_float_nullary: +; CHECK-NEXT: (setlocal @0 (global $float_nullary)) +; CHECK-NEXT: (setlocal @1 (call @0)) +; CHECK-NEXT: (return @1) +define float @call_float_nullary() { + %r = call float @float_nullary() + ret float %r +} + +; CHECK-LABEL: call_double_nullary: +; CHECK-NEXT: (setlocal @0 (global $double_nullary)) +; CHECK-NEXT: (setlocal @1 (call @0)) +; CHECK-NEXT: (return @1) +define double @call_double_nullary() { + %r = call double @double_nullary() + ret double %r } +; CHECK-LABEL: call_i32_unary: +; CHECK-NEXT: (setlocal @0 (argument 0)) +; CHECK-NEXT: (setlocal @1 (global $i32_unary)) +; CHECK-NEXT: (setlocal @2 (call @1 @0)) +; CHECK-NEXT: (return @2) +define i32 @call_i32_unary(i32 %a) { + %r = call i32 @i32_unary(i32 %a) + ret i32 %r +} -; tail call -; multiple args -; interesting returns (int, float, struct, multiple) -; vararg +; FIXME test the following: +; - Functions without return. +; - More argument combinations. +; - Tail call. +; - Interesting returns (struct, multiple). +; - Vararg.