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). Conversions are additionally prefixed with their input type 100 // (before a double underscore). 101 static std::string OpcodeName(const WebAssemblyInstrInfo *TII, 102 const MachineInstr *MI) { 103 std::string N(StringRef(TII->getName(MI->getOpcode())).lower()); 104 std::string::size_type Len = N.length(); 105 std::string::size_type Under = N.rfind('_'); 106 bool HasType = std::string::npos != Under; 107 std::string::size_type NameEnd = HasType ? Under : Len; 108 std::string Name(&N[0], &N[NameEnd]); 109 if (!HasType) 110 return Name; 111 for (const char *typelessOpcode : { "return", "call", "br_if" }) 112 if (Name == typelessOpcode) 113 return Name; 114 std::string Type(&N[NameEnd + 1], &N[Len]); 115 std::string::size_type DoubleUnder = Name.find("__"); 116 bool IsConv = std::string::npos != DoubleUnder; 117 if (!IsConv) 118 return Type + '.' + Name; 119 std::string InType(&Name[0], &Name[DoubleUnder]); 120 return Type + '.' + std::string(&Name[DoubleUnder + 2], &Name[NameEnd]) + 121 '/' + InType; 122 } 123 124 static std::string toSymbol(StringRef S) { return ("$" + S).str(); } 125 126 std::string WebAssemblyAsmPrinter::getRegTypeName(unsigned RegNo) const { 127 const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); 128 for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64}) 129 if (TRC->hasType(T)) 130 return EVT(T).getEVTString(); 131 DEBUG(errs() << "Unknown type for register number: " << RegNo); 132 llvm_unreachable("Unknown register type"); 133 return "?"; 134 } 135 136 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) { 137 static const size_t BufBytes = 128; 138 char buf[BufBytes]; 139 if (FP.isNaN()) 140 assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) || 141 FP.bitwiseIsEqual( 142 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) && 143 "convertToHexString handles neither SNaN nor NaN payloads"); 144 // Use C99's hexadecimal floating-point representation. 145 auto Written = FP.convertToHexString( 146 buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven); 147 (void)Written; 148 assert(Written != 0); 149 assert(Written < BufBytes); 150 return buf; 151 } 152 153 std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { 154 unsigned RegNo = MO.getReg(); 155 if (TargetRegisterInfo::isPhysicalRegister(RegNo)) 156 return WebAssemblyInstPrinter::getRegisterName(RegNo); 157 158 // WebAssembly arguments and local variables are in the same index space, and 159 // there are no explicit varargs, so we just add the number of arguments to 160 // the virtual register number to get the local variable number. 161 return utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs); 162 } 163 164 std::string WebAssemblyAsmPrinter::argToString(const MachineOperand &MO) { 165 unsigned ArgNo = MO.getImm(); 166 // Same as above, but we don't need to add NumArgs here. 167 return utostr(ArgNo); 168 } 169 170 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const { 171 switch (Ty->getTypeID()) { 172 default: 173 break; 174 // Treat all pointers as the underlying integer into linear memory. 175 case Type::PointerTyID: 176 switch (getPointerSize()) { 177 case 4: 178 return "i32"; 179 case 8: 180 return "i64"; 181 default: 182 llvm_unreachable("unsupported pointer size"); 183 } 184 break; 185 case Type::FloatTyID: 186 return "f32"; 187 case Type::DoubleTyID: 188 return "f64"; 189 case Type::IntegerTyID: 190 switch (Ty->getIntegerBitWidth()) { 191 case 8: 192 return "i8"; 193 case 16: 194 return "i16"; 195 case 32: 196 return "i32"; 197 case 64: 198 return "i64"; 199 default: 200 break; 201 } 202 } 203 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n'); 204 llvm_unreachable("invalid type"); 205 return "<invalid>"; 206 } 207 208 //===----------------------------------------------------------------------===// 209 // WebAssemblyAsmPrinter Implementation. 210 //===----------------------------------------------------------------------===// 211 212 void WebAssemblyAsmPrinter::EmitConstantPool() { 213 assert(MF->getConstantPool()->getConstants().empty() && 214 "WebAssembly disables constant pools"); 215 } 216 217 void WebAssemblyAsmPrinter::EmitJumpTableInfo() { 218 // Nothing to do; jump tables are incorporated into the instruction stream. 219 } 220 221 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 222 const Function *F = MF->getFunction(); 223 Type *Rt = F->getReturnType(); 224 SmallString<128> Str; 225 raw_svector_ostream OS(Str); 226 bool First = true; 227 228 if (!Rt->isVoidTy() || !F->arg_empty()) { 229 for (const Argument &A : F->args()) { 230 OS << (First ? "" : "\n") << "\t.param " << toString(A.getType()); 231 First = false; 232 } 233 if (!Rt->isVoidTy()) { 234 OS << (First ? "" : "\n") << "\t.result " << toString(Rt); 235 First = false; 236 } 237 } 238 239 bool FirstVReg = true; 240 for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) { 241 unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx); 242 // FIXME: Don't skip dead virtual registers for now: that would require 243 // remapping all locals' numbers. 244 //if (!MRI->use_empty(VReg)) { 245 if (FirstVReg) { 246 OS << (First ? "" : "\n") << "\t.local "; 247 First = false; 248 } 249 OS << (FirstVReg ? "" : ", ") << getRegTypeName(VReg); 250 FirstVReg = false; 251 //} 252 } 253 254 if (!First) 255 OutStreamer->EmitRawText(OS.str()); 256 AsmPrinter::EmitFunctionBodyStart(); 257 } 258 259 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 260 DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 261 SmallString<128> Str; 262 raw_svector_ostream OS(Str); 263 264 unsigned NumDefs = MI->getDesc().getNumDefs(); 265 assert(NumDefs <= 1 && 266 "Instructions with multiple result values not implemented"); 267 268 OS << '\t'; 269 270 switch (MI->getOpcode()) { 271 case TargetOpcode::COPY: 272 OS << "get_local push, " << regToString(MI->getOperand(1)); 273 break; 274 case WebAssembly::GLOBAL: 275 // TODO: wasm64 276 OS << "i32.const push, " << toSymbol(MI->getOperand(1).getGlobal()->getName()); 277 break; 278 case WebAssembly::ARGUMENT_I32: 279 case WebAssembly::ARGUMENT_I64: 280 case WebAssembly::ARGUMENT_F32: 281 case WebAssembly::ARGUMENT_F64: 282 OS << "get_local push, " << argToString(MI->getOperand(1)); 283 break; 284 case WebAssembly::Const_I32: 285 OS << "i32.const push, " << MI->getOperand(1).getImm(); 286 break; 287 case WebAssembly::Const_I64: 288 OS << "i64.const push, " << MI->getOperand(1).getImm(); 289 break; 290 case WebAssembly::Const_F32: 291 OS << "f32.const push, " << toString(MI->getOperand(1).getFPImm()->getValueAPF()); 292 break; 293 case WebAssembly::Const_F64: 294 OS << "f64.const push, " << toString(MI->getOperand(1).getFPImm()->getValueAPF()); 295 break; 296 default: { 297 OS << OpcodeName(TII, MI); 298 bool NeedComma = false; 299 bool DefsPushed = false; 300 if (NumDefs != 0 && !MI->isCall()) { 301 OS << " push"; 302 NeedComma = true; 303 DefsPushed = true; 304 } 305 for (const MachineOperand &MO : MI->uses()) { 306 if (MO.isReg() && MO.isImplicit()) 307 continue; 308 if (NeedComma) 309 OS << ','; 310 NeedComma = true; 311 OS << ' '; 312 switch (MO.getType()) { 313 default: 314 llvm_unreachable("unexpected machine operand type"); 315 case MachineOperand::MO_Register: 316 OS << "(get_local " << regToString(MO) << ')'; 317 break; 318 case MachineOperand::MO_Immediate: 319 OS << MO.getImm(); 320 break; 321 case MachineOperand::MO_FPImmediate: 322 OS << toString(MO.getFPImm()->getValueAPF()); 323 break; 324 case MachineOperand::MO_GlobalAddress: 325 OS << toSymbol(MO.getGlobal()->getName()); 326 break; 327 case MachineOperand::MO_MachineBasicBlock: 328 OS << toSymbol(MO.getMBB()->getSymbol()->getName()); 329 break; 330 } 331 if (NumDefs != 0 && !DefsPushed) { 332 // Special-case for calls; print the push after the callee. 333 assert(MI->isCall()); 334 OS << ", push"; 335 DefsPushed = true; 336 } 337 } 338 break; 339 } 340 } 341 342 OutStreamer->EmitRawText(OS.str()); 343 344 if (NumDefs != 0) { 345 SmallString<128> Str; 346 raw_svector_ostream OS(Str); 347 const MachineOperand &Operand = MI->getOperand(0); 348 OS << "\tset_local " << regToString(Operand) << ", pop"; 349 OutStreamer->EmitRawText(OS.str()); 350 } 351 } 352 353 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) { 354 SmallString<128> Str; 355 raw_svector_ostream OS(Str); 356 for (const Function &F : M) 357 if (F.isDeclarationForLinker()) { 358 assert(F.hasName() && "imported functions must have a name"); 359 if (F.getName().startswith("llvm.")) 360 continue; 361 if (Str.empty()) 362 OS << "\t.imports\n"; 363 Type *Rt = F.getReturnType(); 364 OS << "\t.import " << toSymbol(F.getName()) << " \"\" \"" << F.getName() 365 << "\" (param"; 366 for (const Argument &A : F.args()) 367 OS << ' ' << toString(A.getType()); 368 OS << ')'; 369 if (!Rt->isVoidTy()) 370 OS << " (result " << toString(Rt) << ')'; 371 OS << '\n'; 372 } 373 OutStreamer->EmitRawText(OS.str()); 374 } 375 376 // Force static initialization. 377 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 378 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32); 379 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64); 380 } 381