Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.h @@ -32,6 +32,9 @@ 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); Index: lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp =================================================================== --- lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp +++ lib/Target/WebAssembly/InstPrinter/WebAssemblyInstPrinter.cpp @@ -44,3 +44,16 @@ 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); + } +} Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -85,7 +85,10 @@ 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); @@ -131,6 +134,9 @@ assert(Written < BufBytes); OS << ' ' << buf; } break; + case MachineOperand::MO_GlobalAddress: { + OS << ' ' << toSymbol(MO.getGlobal()->getName()); + } break; } OS << ')'; Index: lib/Target/WebAssembly/WebAssemblyISD.def =================================================================== --- lib/Target/WebAssembly/WebAssemblyISD.def +++ lib/Target/WebAssembly/WebAssemblyISD.def @@ -1,4 +1,4 @@ -//===- WebAssemblyInstrCall.td-WebAssembly Call codegen support -*- tablegen -*- +//- WebAssemblyISD.def - WebAssembly ISD ---------------------------*- C++ -*-// // // The LLVM Compiler Infrastructure // @@ -8,14 +8,15 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief WebAssembly Call operand code-gen constructs. +/// \brief This file describes the various WebAssembly ISD node types. /// //===----------------------------------------------------------------------===// -/* - * TODO(jfb): Add the following. - * - * call_direct: call function directly - * call_indirect: call function indirectly - * addressof: obtain a function pointer value for a given function - */ +// 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... Index: lib/Target/WebAssembly/WebAssemblyISelLowering.h =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.h +++ lib/Target/WebAssembly/WebAssemblyISelLowering.h @@ -24,10 +24,9 @@ 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 @@ -45,25 +44,29 @@ /// right decision when generating code for different targets. const WebAssemblySubtarget *Subtarget; + 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; }; } // end namespace llvm Index: lib/Target/WebAssembly/WebAssemblyISelLowering.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ 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 @@ 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 @@ // 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); @@ -122,6 +127,13 @@ } } +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(); @@ -130,9 +142,13 @@ 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; } @@ -151,6 +167,70 @@ DiagnosticInfoUnsupported(DL, *MF.getFunction(), msg, SDValue())); } +SDValue +WebAssemblyTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc DL = CLI.DL; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + MachineFunction &MF = DAG.getMachineFunction(); + + CallingConv::ID CallConv = CLI.CallConv; + if (CallConv != CallingConv::C) + fail(DL, DAG, "WebAssembly doesn't support non-C calling conventions"); + if (CLI.IsTailCall || MF.getTarget().Options.GuaranteedTailCallOpt) + fail(DL, DAG, "WebAssembly doesn't support tail call yet"); + if (CLI.IsPatchPoint) + fail(DL, DAG, "WebAssembly doesn't support patch point yet"); + + SmallVectorImpl &Outs = CLI.Outs; + SmallVectorImpl &OutVals = CLI.OutVals; + bool IsStructRet = (Outs.empty()) ? false : Outs[0].Flags.isSRet(); + if (IsStructRet) + fail(DL, DAG, "WebAssembly doesn't support struct return yet"); + if (Outs.size() > 1) + fail(DL, DAG, "WebAssembly doesn't support more than 1 returned value yet"); + + SmallVectorImpl &Ins = CLI.Ins; + bool IsVarArg = CLI.IsVarArg; + if (IsVarArg) + fail(DL, DAG, "WebAssembly doesn't support varargs yet"); + // Analyze operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + CCState CCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + unsigned NumBytes = CCInfo.getNextStackOffset(); + + auto PtrVT = getPointerTy(MF.getDataLayout()); + 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(Callee); + Ops.append(OutVals.begin(), OutVals.end()); + + SmallVector Tys; + for (const auto &In : Ins) + Tys.push_back(In.VT); + Tys.push_back(MVT::Other); + 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 = DAG.getCALLSEQ_END(Chain, NB, Zero, SDValue(), DL); + + return Chain; +} + bool WebAssemblyTargetLowering::CanLowerReturn( CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, const SmallVectorImpl &Outs, LLVMContext &Context) const { @@ -227,9 +307,34 @@ } //===----------------------------------------------------------------------===// -// 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 //===----------------------------------------------------------------------===// Index: lib/Target/WebAssembly/WebAssemblyInstrCall.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrCall.td +++ 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. +def : Pseudo<(outs), (ins i64imm:$amt), + [(WebAssemblycallseq_start timm:$amt)], + "#ADJCALLSTACKDOWN $amt">; +def : Pseudo<(outs), (ins i64imm:$amt1, i64imm:$amt2), + [(WebAssemblycallseq_end timm:$amt1, timm:$amt2)], + "#ADJCALLSTACKUP $amt1 $amt2">; + +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. * Index: lib/Target/WebAssembly/WebAssemblyInstrControl.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrControl.td +++ 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 Index: lib/Target/WebAssembly/WebAssemblyInstrFormats.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrFormats.td +++ 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 @@ let Constraints = cstr; } -// Normal instructions +// Normal instructions. class I pattern, string cstr = ""> : WebAssemblyInst { dag OutOperandList = oops; @@ -28,6 +28,14 @@ let Pattern = pattern; } +// Pseudo instructions. +class Pseudo pattern, string asmstr, + string cstr = ""> + : I { + let isPseudo = 1; + let AsmString = asmstr; +} + // Unary and binary instructions, for the local types that WebAssembly supports. multiclass UnaryInt { def _I32 : I<(outs Int32:$dst), (ins Int32:$src), Index: lib/Target/WebAssembly/WebAssemblyInstrInfo.td =================================================================== --- lib/Target/WebAssembly/WebAssemblyInstrInfo.td +++ lib/Target/WebAssembly/WebAssemblyInstrInfo.td @@ -25,18 +25,34 @@ // 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 @@ * set_local: set the current value of a local variable */ +def global : Operand; + //===----------------------------------------------------------------------===// // WebAssembly Instruction Format Definitions. //===----------------------------------------------------------------------===// @@ -74,6 +92,10 @@ 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. //===----------------------------------------------------------------------===// Index: test/CodeGen/WebAssembly/call.ll =================================================================== --- /dev/null +++ test/CodeGen/WebAssembly/call.ll @@ -0,0 +1,65 @@ +; RUN: llc < %s -asm-verbose=false | FileCheck %s + +; Test that basic call operations assemble as expected. + +target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128" +target triple = "wasm32-unknown-unknown" + +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 +} + +; FIXME test the following: +; - Functions without return. +; - More argument combinations. +; - Tail call. +; - Interesting returns (struct, multiple). +; - Vararg.