1 //===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// \brief This file contains a printer that converts from our internal 12 /// representation of machine-dependent LLVM code to the WebAssembly assembly 13 /// language. 14 /// 15 //===----------------------------------------------------------------------===// 16 17 #include "WebAssembly.h" 18 #include "WebAssemblyMachineFunctionInfo.h" 19 #include "WebAssemblyRegisterInfo.h" 20 #include "WebAssemblySubtarget.h" 21 #include "InstPrinter/WebAssemblyInstPrinter.h" 22 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 23 24 #include "llvm/ADT/SmallString.h" 25 #include "llvm/ADT/StringExtras.h" 26 #include "llvm/CodeGen/AsmPrinter.h" 27 #include "llvm/CodeGen/MachineConstantPool.h" 28 #include "llvm/CodeGen/MachineInstr.h" 29 #include "llvm/IR/DataLayout.h" 30 #include "llvm/IR/DebugInfo.h" 31 #include "llvm/MC/MCStreamer.h" 32 #include "llvm/MC/MCSymbol.h" 33 #include "llvm/Support/Debug.h" 34 #include "llvm/Support/TargetRegistry.h" 35 #include "llvm/Support/raw_ostream.h" 36 37 using namespace llvm; 38 39 #define DEBUG_TYPE "asm-printer" 40 41 namespace { 42 43 class WebAssemblyAsmPrinter final : public AsmPrinter { 44 const WebAssemblyInstrInfo *TII; 45 const MachineRegisterInfo *MRI; 46 unsigned NumArgs; 47 48 public: 49 WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) 50 : AsmPrinter(TM, std::move(Streamer)), TII(nullptr), MRI(nullptr) {} 51 52 private: 53 const char *getPassName() const override { 54 return "WebAssembly Assembly Printer"; 55 } 56 57 //===------------------------------------------------------------------===// 58 // MachineFunctionPass Implementation. 59 //===------------------------------------------------------------------===// 60 61 void getAnalysisUsage(AnalysisUsage &AU) const override { 62 AsmPrinter::getAnalysisUsage(AU); 63 } 64 65 bool runOnMachineFunction(MachineFunction &MF) override { 66 const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>(); 67 TII = Subtarget.getInstrInfo(); 68 MRI = &MF.getRegInfo(); 69 NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments(); 70 return AsmPrinter::runOnMachineFunction(MF); 71 } 72 73 //===------------------------------------------------------------------===// 74 // AsmPrinter Implementation. 75 //===------------------------------------------------------------------===// 76 77 void EmitJumpTableInfo() override; 78 void EmitConstantPool() override; 79 void EmitFunctionBodyStart() override; 80 void EmitInstruction(const MachineInstr *MI) override; 81 void EmitEndOfAsmFile(Module &M) override; 82 83 std::string getRegTypeName(unsigned RegNo) const; 84 static std::string toString(const APFloat &APF); 85 const char *toString(Type *Ty) const; 86 std::string regToString(const MachineOperand &MO); 87 std::string argToString(const MachineOperand &MO); 88 }; 89 90 } // end anonymous namespace 91 92 //===----------------------------------------------------------------------===// 93 // Helpers. 94 //===----------------------------------------------------------------------===// 95 96 // Operand type (if any), followed by the lower-case version of the opcode's 97 // name matching the names WebAssembly opcodes are expected to have. The 98 // tablegen names are uppercase and suffixed with their type (after an 99 // underscore). 100 static std::string OpcodeName(const WebAssemblyInstrInfo *TII, 101 const MachineInstr *MI) { 102 std::string N(StringRef(TII->getName(MI->getOpcode())).lower()); 103 std::string::size_type Len = N.length(); 104 std::string::size_type Under = N.rfind('_'); 105 bool HasType = std::string::npos != Under; 106 std::string::size_type NameEnd = HasType ? Under : Len; 107 std::string Name(&N[0], &N[NameEnd]); 108 if (!HasType) 109 return Name; 110 for (const char *typelessOpcode : { "return", "call", "br_if" }) 111 if (Name == typelessOpcode) 112 return Name; 113 return std::string(&N[NameEnd + 1], &N[Len]) + '.' + Name; 114 } 115 116 static std::string toSymbol(StringRef S) { return ("$" + S).str(); } 117 118 std::string WebAssemblyAsmPrinter::getRegTypeName(unsigned RegNo) const { 119 const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); 120 for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) 121 if (TRC->hasType(T)) 122 return EVT(T).getEVTString(); 123 DEBUG(errs() << "Unknown type for register number: " << RegNo); 124 llvm_unreachable("Unknown register type"); 125 return "?"; 126 } 127 128 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) { 129 static const size_t BufBytes = 128; 130 char buf[BufBytes]; 131 if (FP.isNaN()) 132 assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) || 133 FP.bitwiseIsEqual( 134 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) && 135 "convertToHexString handles neither SNaN nor NaN payloads"); 136 // Use C99's hexadecimal floating-point representation. 137 auto Written = FP.convertToHexString( 138 buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven); 139 (void)Written; 140 assert(Written != 0); 141 assert(Written < BufBytes); 142 return buf; 143 } 144 145 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { 146 unsigned RegNo = MO.getReg(); 147 if (TargetRegisterInfo::isPhysicalRegister(RegNo)) 148 return WebAssemblyInstPrinter::getRegisterName(RegNo); 149 150 // WebAssembly arguments and local variables are in the same index space, and 151 // there are no explicit varargs, so we just add the number of arguments to 152 // the virtual register number to get the local variable number. 153 return utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs); 154 } 155 156 std::string WebAssemblyAsmPrinter::argToString(const MachineOperand &MO) { 157 unsigned ArgNo = MO.getImm(); 158 // Same as above, but we don't need to add NumArgs here. 159 return utostr(ArgNo); 160 } 161 162 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const { 163 switch (Ty->getTypeID()) { 164 default: 165 break; 166 // Treat all pointers as the underlying integer into linear memory. 167 case Type::PointerTyID: 168 switch (getPointerSize()) { 169 case 4: 170 return "i32"; 171 case 8: 172 return "i64"; 173 default: 174 llvm_unreachable("unsupported pointer size"); 175 } 176 break; 177 case Type::FloatTyID: 178 return "f32"; 179 case Type::DoubleTyID: 180 return "f64"; 181 case Type::IntegerTyID: 182 switch (Ty->getIntegerBitWidth()) { 183 case 8: 184 return "i8"; 185 case 16: 186 return "i16"; 187 case 32: 188 return "i32"; 189 case 64: 190 return "i64"; 191 default: 192 break; 193 } 194 } 195 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n'); 196 llvm_unreachable("invalid type"); 197 return "<invalid>"; 198 } 199 200 //===----------------------------------------------------------------------===// 201 // WebAssemblyAsmPrinter Implementation. 202 //===----------------------------------------------------------------------===// 203 204 void WebAssemblyAsmPrinter::EmitConstantPool() { 205 assert(MF->getConstantPool()->getConstants().empty() && 206 "WebAssembly disables constant pools"); 207 } 208 209 void WebAssemblyAsmPrinter::EmitJumpTableInfo() { 210 // Nothing to do; jump tables are incorporated into the instruction stream. 211 } 212 213 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 214 const Function *F = MF->getFunction(); 215 Type *Rt = F->getReturnType(); 216 SmallString<128> Str; 217 raw_svector_ostream OS(Str); 218 bool First = true; 219 220 if (!Rt->isVoidTy() || !F->arg_empty()) { 221 for (const Argument &A : F->args()) { 222 OS << (First ? "" : "\n") << "\t.param " << toString(A.getType()); 223 First = false; 224 } 225 if (!Rt->isVoidTy()) { 226 OS << (First ? "" : "\n") << "\t.result " << toString(Rt); 227 First = false; 228 } 229 } 230 231 bool FirstVReg = true; 232 for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) { 233 unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx); 234 // FIXME: Don't skip dead virtual registers for now: that would require 235 // remapping all locals' numbers. 236 //if (!MRI->use_empty(VReg)) { 237 if (FirstVReg) { 238 OS << (First ? "" : "\n") << "\t.local "; 239 First = false; 240 } 241 OS << (FirstVReg ? "" : ", ") << getRegTypeName(VReg); 242 FirstVReg = false; 243 //} 244 } 245 246 if (!First) 247 OutStreamer->EmitRawText(OS.str()); 248 AsmPrinter::EmitFunctionBodyStart(); 249 } 250 251 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 252 DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 253 SmallString<128> Str; 254 raw_svector_ostream OS(Str); 255 256 unsigned NumDefs = MI->getDesc().getNumDefs(); 257 assert(NumDefs <= 1 && 258 "Instructions with multiple result values not implemented"); 259 260 OS << '\t'; 261 262 switch (MI->getOpcode()) { 263 case TargetOpcode::COPY: 264 OS << "get_local " << regToString(MI->getOperand(1)); 265 break; 266 case WebAssembly::GLOBAL: 267 // TODO: wasm64 268 OS << "i32.const " << toSymbol(MI->getOperand(1).getGlobal()->getName()); 269 break; 270 case WebAssembly::ARGUMENT_I32: 271 case WebAssembly::ARGUMENT_I64: 272 case WebAssembly::ARGUMENT_F32: 273 case WebAssembly::ARGUMENT_F64: 274 OS << "get_local " << argToString(MI->getOperand(1)); 275 break; 276 case WebAssembly::Immediate_I32: 277 OS << "i32.const " << MI->getOperand(1).getImm(); 278 break; 279 case WebAssembly::Immediate_I64: 280 OS << "i64.const " << MI->getOperand(1).getImm(); 281 break; 282 case WebAssembly::Immediate_F32: 283 OS << "f32.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF()); 284 break; 285 case WebAssembly::Immediate_F64: 286 OS << "f64.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF()); 287 break; 288 default: { 289 OS << OpcodeName(TII, MI); 290 bool NeedComma = false; 291 for (const MachineOperand &MO : MI->uses()) { 292 if (MO.isReg() && MO.isImplicit()) 293 continue; 294 if (NeedComma) 295 OS << ','; 296 NeedComma = true; 297 OS << ' '; 298 switch (MO.getType()) { 299 default: 300 llvm_unreachable("unexpected machine operand type"); 301 case MachineOperand::MO_Register: 302 OS << "(get_local " << regToString(MO) << ')'; 303 break; 304 case MachineOperand::MO_Immediate: 305 OS << MO.getImm(); 306 break; 307 case MachineOperand::MO_FPImmediate: 308 OS << toString(MO.getFPImm()->getValueAPF()); 309 break; 310 case MachineOperand::MO_GlobalAddress: 311 OS << toSymbol(MO.getGlobal()->getName()); 312 break; 313 case MachineOperand::MO_MachineBasicBlock: 314 OS << toSymbol(MO.getMBB()->getSymbol()->getName()); 315 break; 316 } 317 } 318 break; 319 } 320 } 321 322 OutStreamer->EmitRawText(OS.str()); 323 324 if (NumDefs != 0) { 325 SmallString<128> Str; 326 raw_svector_ostream OS(Str); 327 const MachineOperand &Operand = MI->getOperand(0); 328 OS << "\tset_local " << regToString(Operand) << ", pop"; 329 OutStreamer->EmitRawText(OS.str()); 330 } 331 } 332 333 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { 334 SmallString<128> Str; 335 raw_svector_ostream OS(Str); 336 for (const Function &F : M) 337 if (F.isDeclarationForLinker()) { 338 assert(F.hasName() && "imported functions must have a name"); 339 if (F.getName().startswith("llvm.")) 340 continue; 341 if (Str.empty()) 342 OS << "\t.imports\n"; 343 Type *Rt = F.getReturnType(); 344 OS << "\t.import " << toSymbol(F.getName()) << " \"\" \"" << F.getName() 345 << "\""; 346 for (const Argument &A : F.args()) 347 OS << " (param " << toString(A.getType()) << ')'; 348 if (!Rt->isVoidTy()) 349 OS << " (result " << toString(Rt) << ')'; 350 OS << '\n'; 351 } 352 OutStreamer->EmitRawText(OS.str()); 353 } 354 355 // Force static initialization. 356 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 357 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32); 358 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64); 359 } 360