brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.9 KiB · e48283a Raw
377 lines · cpp
1// WebAssemblyMCInstLower.cpp - Convert WebAssembly MachineInstr to an MCInst //2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains code to lower WebAssembly MachineInstrs to their11/// corresponding MCInst records.12///13//===----------------------------------------------------------------------===//14 15#include "WebAssemblyMCInstLower.h"16#include "MCTargetDesc/WebAssemblyMCAsmInfo.h"17#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"18#include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"19#include "TargetInfo/WebAssemblyTargetInfo.h"20#include "Utils/WebAssemblyTypeUtilities.h"21#include "WebAssemblyAsmPrinter.h"22#include "WebAssemblyMachineFunctionInfo.h"23#include "WebAssemblyUtilities.h"24#include "llvm/ADT/APInt.h"25#include "llvm/ADT/SmallVector.h"26#include "llvm/BinaryFormat/Wasm.h"27#include "llvm/CodeGen/AsmPrinter.h"28#include "llvm/CodeGen/MachineFunction.h"29#include "llvm/CodeGen/MachineOperand.h"30#include "llvm/IR/Constants.h"31#include "llvm/MC/MCAsmInfo.h"32#include "llvm/MC/MCContext.h"33#include "llvm/MC/MCExpr.h"34#include "llvm/MC/MCInst.h"35#include "llvm/MC/MCSymbolWasm.h"36#include "llvm/Support/ErrorHandling.h"37#include "llvm/Support/raw_ostream.h"38 39using namespace llvm;40 41// This disables the removal of registers when lowering into MC, as required42// by some current tests.43static cl::opt<bool>44    WasmKeepRegisters("wasm-keep-registers", cl::Hidden,45                      cl::desc("WebAssembly: output stack registers in"46                               " instruction output for test purposes only."),47                      cl::init(false));48 49static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI);50 51MCSymbol *52WebAssemblyMCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {53  const GlobalValue *Global = MO.getGlobal();54  if (!isa<Function>(Global)) {55    auto *WasmSym = static_cast<MCSymbolWasm *>(Printer.getSymbol(Global));56    // If the symbol doesn't have an explicit WasmSymbolType yet and the57    // GlobalValue is actually a WebAssembly global, then ensure the symbol is a58    // WASM_SYMBOL_TYPE_GLOBAL.59    if (WebAssembly::isWasmVarAddressSpace(Global->getAddressSpace()) &&60        !WasmSym->getType()) {61      const MachineFunction &MF = *MO.getParent()->getParent()->getParent();62      const TargetMachine &TM = MF.getTarget();63      const Function &CurrentFunc = MF.getFunction();64      Type *GlobalVT = Global->getValueType();65      SmallVector<MVT, 1> VTs;66      computeLegalValueVTs(CurrentFunc, TM, GlobalVT, VTs);67 68      WebAssembly::wasmSymbolSetType(WasmSym, GlobalVT, VTs);69    }70    return WasmSym;71  }72 73  const auto *FuncTy = cast<FunctionType>(Global->getValueType());74  const MachineFunction &MF = *MO.getParent()->getParent()->getParent();75  const TargetMachine &TM = MF.getTarget();76  const Function &CurrentFunc = MF.getFunction();77 78  SmallVector<MVT, 1> ResultMVTs;79  SmallVector<MVT, 4> ParamMVTs;80  const auto *const F = dyn_cast<Function>(Global);81  computeSignatureVTs(FuncTy, F, CurrentFunc, TM, ParamMVTs, ResultMVTs);82  auto Signature = signatureFromMVTs(Ctx, ResultMVTs, ParamMVTs);83 84  bool InvokeDetected = false;85  auto *WasmSym = Printer.getMCSymbolForFunction(F, Signature, InvokeDetected);86  WasmSym->setSignature(Signature);87  WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);88  return WasmSym;89}90 91MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(92    const MachineOperand &MO) const {93  return Printer.getOrCreateWasmSymbol(MO.getSymbolName());94}95 96MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,97                                                     MCSymbol *Sym) const {98  auto Spec = WebAssembly::S_None;99  unsigned TargetFlags = MO.getTargetFlags();100 101  switch (TargetFlags) {102    case WebAssemblyII::MO_NO_FLAG:103      break;104    case WebAssemblyII::MO_GOT_TLS:105      Spec = WebAssembly::S_GOT_TLS;106      break;107    case WebAssemblyII::MO_GOT:108      Spec = WebAssembly::S_GOT;109      break;110    case WebAssemblyII::MO_MEMORY_BASE_REL:111      Spec = WebAssembly::S_MBREL;112      break;113    case WebAssemblyII::MO_TLS_BASE_REL:114      Spec = WebAssembly::S_TLSREL;115      break;116    case WebAssemblyII::MO_TABLE_BASE_REL:117      Spec = WebAssembly::S_TBREL;118      break;119    default:120      llvm_unreachable("Unknown target flag on GV operand");121  }122 123  const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Spec, Ctx);124 125  if (MO.getOffset() != 0) {126    const auto *WasmSym = static_cast<const MCSymbolWasm *>(Sym);127    if (TargetFlags == WebAssemblyII::MO_GOT)128      report_fatal_error("GOT symbol references do not support offsets");129    if (WasmSym->isFunction())130      report_fatal_error("Function addresses with offsets not supported");131    if (WasmSym->isGlobal())132      report_fatal_error("Global indexes with offsets not supported");133    if (WasmSym->isTag())134      report_fatal_error("Tag indexes with offsets not supported");135    if (WasmSym->isTable())136      report_fatal_error("Table indexes with offsets not supported");137 138    Expr = MCBinaryExpr::createAdd(139        Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);140  }141 142  return MCOperand::createExpr(Expr);143}144 145MCOperand WebAssemblyMCInstLower::lowerTypeIndexOperand(146    SmallVectorImpl<wasm::ValType> &&Returns,147    SmallVectorImpl<wasm::ValType> &&Params) const {148  auto Signature = Ctx.createWasmSignature();149  Signature->Returns = std::move(Returns);150  Signature->Params = std::move(Params);151  auto *Sym =152      static_cast<MCSymbolWasm *>(Printer.createTempSymbol("typeindex"));153  Sym->setSignature(Signature);154  Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);155  const MCExpr *Expr =156      MCSymbolRefExpr::create(Sym, WebAssembly::S_TYPEINDEX, Ctx);157  return MCOperand::createExpr(Expr);158}159 160MCOperand161WebAssemblyMCInstLower::lowerEncodedFunctionSignature(const APInt &Sig) const {162  // For APInt a word is 64 bits on all architectures, see definition in APInt.h163  auto NumWords = Sig.getNumWords();164  SmallVector<wasm::ValType, 4> Params;165  SmallVector<wasm::ValType, 2> Returns;166 167  int Idx = NumWords;168  auto GetWord = [&Idx, &Sig]() {169    Idx--;170    return Sig.extractBitsAsZExtValue(64, 64 * Idx);171  };172  // Annoying special case: if getSignificantBits() <= 64 then InstrEmitter will173  // emit an Imm instead of a CImm. It simplifies WebAssemblyMCInstLower if we174  // always emit a CImm. So xor NParams with 0x7ffffff to ensure175  // getSignificantBits() > 64176  // See encodeFunctionSignature in WebAssemblyISelDAGtoDAG.cpp177  int NReturns = GetWord() ^ 0x7ffffff;178  for (int I = 0; I < NReturns; I++) {179    Returns.push_back(static_cast<wasm::ValType>(GetWord()));180  }181  int NParams = GetWord();182  for (int I = 0; I < NParams; I++) {183    Params.push_back(static_cast<wasm::ValType>(GetWord()));184  }185  return lowerTypeIndexOperand(std::move(Returns), std::move(Params));186}187 188static void getFunctionReturns(const MachineInstr *MI,189                               SmallVectorImpl<wasm::ValType> &Returns) {190  const Function &F = MI->getMF()->getFunction();191  const TargetMachine &TM = MI->getMF()->getTarget();192  Type *RetTy = F.getReturnType();193  SmallVector<MVT, 4> CallerRetTys;194  computeLegalValueVTs(F, TM, RetTy, CallerRetTys);195  valTypesFromMVTs(CallerRetTys, Returns);196}197 198void WebAssemblyMCInstLower::lower(const MachineInstr *MI,199                                   MCInst &OutMI) const {200  OutMI.setOpcode(MI->getOpcode());201 202  const MCInstrDesc &Desc = MI->getDesc();203  unsigned NumVariadicDefs = MI->getNumExplicitDefs() - Desc.getNumDefs();204  const MachineFunction *MF = MI->getMF();205  const auto &TLI =206      *MF->getSubtarget<WebAssemblySubtarget>().getTargetLowering();207  wasm::ValType PtrTy = TLI.getPointerTy(MF->getDataLayout()) == MVT::i32208                            ? wasm::ValType::I32209                            : wasm::ValType::I64;210 211  for (unsigned I = 0, E = MI->getNumOperands(); I != E; ++I) {212    const MachineOperand &MO = MI->getOperand(I);213 214    MCOperand MCOp;215    switch (MO.getType()) {216    default:217      MI->print(errs());218      llvm_unreachable("unknown operand type");219    case MachineOperand::MO_MachineBasicBlock:220      MI->print(errs());221      llvm_unreachable("MachineBasicBlock operand should have been rewritten");222    case MachineOperand::MO_Register: {223      // Ignore all implicit register operands.224      if (MO.isImplicit())225        continue;226      const WebAssemblyFunctionInfo &MFI =227          *MI->getParent()->getParent()->getInfo<WebAssemblyFunctionInfo>();228      unsigned WAReg = MFI.getWAReg(MO.getReg());229      MCOp = MCOperand::createReg(WAReg);230      break;231    }232    case llvm::MachineOperand::MO_CImmediate: {233      // Lower type index placeholder for ref.test234      // Currently this is the only way that CImmediates show up so panic if we235      // get confused.236      unsigned DescIndex = I - NumVariadicDefs;237      assert(DescIndex < Desc.NumOperands && "unexpected CImmediate operand");238      auto Operands = Desc.operands();239      const MCOperandInfo &Info = Operands[DescIndex];240      assert(Info.OperandType == WebAssembly::OPERAND_TYPEINDEX &&241             "unexpected CImmediate operand");242      (void)Info;243      MCOp = lowerEncodedFunctionSignature(MO.getCImm()->getValue());244      break;245    }246    case MachineOperand::MO_Immediate: {247      unsigned DescIndex = I - NumVariadicDefs;248      if (DescIndex < Desc.NumOperands) {249        auto Operands = Desc.operands();250        const MCOperandInfo &Info = Operands[DescIndex];251        // Replace type index placeholder with actual type index. The type index252        // placeholders are Immediates and have an operand type of253        // OPERAND_TYPEINDEX or OPERAND_SIGNATURE.254        if (Info.OperandType == WebAssembly::OPERAND_TYPEINDEX) {255          // Lower type index placeholder for a CALL_INDIRECT instruction256          SmallVector<wasm::ValType, 4> Returns;257          SmallVector<wasm::ValType, 4> Params;258 259          const MachineRegisterInfo &MRI =260              MI->getParent()->getParent()->getRegInfo();261          for (const MachineOperand &MO : MI->defs())262            Returns.push_back(WebAssembly::regClassToValType(263                MRI.getRegClass(MO.getReg())->getID()));264          for (const MachineOperand &MO : MI->explicit_uses())265            if (MO.isReg())266              Params.push_back(WebAssembly::regClassToValType(267                  MRI.getRegClass(MO.getReg())->getID()));268 269          // call_indirect instructions have a callee operand at the end which270          // doesn't count as a param.271          if (WebAssembly::isCallIndirect(MI->getOpcode()))272            Params.pop_back();273 274          // return_call_indirect instructions have the return type of the275          // caller276          if (MI->getOpcode() == WebAssembly::RET_CALL_INDIRECT)277            getFunctionReturns(MI, Returns);278 279          MCOp = lowerTypeIndexOperand(std::move(Returns), std::move(Params));280          break;281        }282        if (Info.OperandType == WebAssembly::OPERAND_SIGNATURE) {283          // Lower type index placeholder for blocks284          auto BT = static_cast<WebAssembly::BlockType>(MO.getImm());285          assert(BT != WebAssembly::BlockType::Invalid);286          if (BT == WebAssembly::BlockType::Multivalue) {287            SmallVector<wasm::ValType, 2> Returns;288            // Multivalue blocks are emitted in two cases:289            // 1. When the blocks will never be exited and are at the ends of290            //    functions (see291            //    WebAssemblyCFGStackify::fixEndsAtEndOfFunction). In this case292            //    the exact multivalue signature can always be inferred from the293            //    return type of the parent function.294            // 2. (catch_ref ...) clause in try_table instruction. Currently all295            //    tags we support (cpp_exception and c_longjmp) throws a single296            //    pointer, so the multivalue signature for this case will be297            //    (ptr, exnref). Having MO_CATCH_BLOCK_SIG target flags means298            //    this is a destination of a catch_ref.299            if (MO.getTargetFlags() == WebAssemblyII::MO_CATCH_BLOCK_SIG) {300              Returns = {PtrTy, wasm::ValType::EXNREF};301            } else302              getFunctionReturns(MI, Returns);303            MCOp = lowerTypeIndexOperand(std::move(Returns),304                                         SmallVector<wasm::ValType, 4>());305            break;306          }307        }308      }309      MCOp = MCOperand::createImm(MO.getImm());310      break;311    }312    case MachineOperand::MO_FPImmediate: {313      const ConstantFP *Imm = MO.getFPImm();314      const uint64_t BitPattern =315          Imm->getValueAPF().bitcastToAPInt().getZExtValue();316      if (Imm->getType()->isFloatTy())317        MCOp = MCOperand::createSFPImm(static_cast<uint32_t>(BitPattern));318      else if (Imm->getType()->isDoubleTy())319        MCOp = MCOperand::createDFPImm(BitPattern);320      else321        llvm_unreachable("unknown floating point immediate type");322      break;323    }324    case MachineOperand::MO_GlobalAddress:325      MCOp = lowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));326      break;327    case MachineOperand::MO_ExternalSymbol:328      MCOp = lowerSymbolOperand(MO, GetExternalSymbolSymbol(MO));329      break;330    case MachineOperand::MO_MCSymbol:331      assert(MO.getTargetFlags() == 0 &&332             "WebAssembly does not use target flags on MCSymbol");333      MCOp = lowerSymbolOperand(MO, MO.getMCSymbol());334      break;335    }336 337    OutMI.addOperand(MCOp);338  }339 340  if (!WasmKeepRegisters)341    removeRegisterOperands(MI, OutMI);342  else if (Desc.variadicOpsAreDefs())343    OutMI.insert(OutMI.begin(), MCOperand::createImm(MI->getNumExplicitDefs()));344}345 346static void removeRegisterOperands(const MachineInstr *MI, MCInst &OutMI) {347  // Remove all uses of stackified registers to bring the instruction format348  // into its final stack form used thruout MC, and transition opcodes to349  // their _S variant.350  // We do this separate from the above code that still may need these351  // registers for e.g. call_indirect signatures.352  // See comments in lib/Target/WebAssembly/WebAssemblyInstrFormats.td for353  // details.354  // TODO: the code above creates new registers which are then removed here.355  // That code could be slightly simplified by not doing that, though maybe356  // it is simpler conceptually to keep the code above in "register mode"357  // until this transition point.358  // FIXME: we are not processing inline assembly, which contains register359  // operands, because it is used by later target generic code.360  if (MI->isDebugInstr() || MI->isLabel() || MI->isInlineAsm())361    return;362 363  // Transform to _S instruction.364  auto RegOpcode = OutMI.getOpcode();365  auto StackOpcode = WebAssembly::getStackOpcode(RegOpcode);366  assert(StackOpcode != -1 && "Failed to stackify instruction");367  OutMI.setOpcode(StackOpcode);368 369  // Remove register operands.370  for (auto I = OutMI.getNumOperands(); I; --I) {371    auto &MO = OutMI.getOperand(I - 1);372    if (MO.isReg()) {373      OutMI.erase(&MO);374    }375  }376}377