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 unsigned NumArgs; 46 47 public: 48 WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) 49 : AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {} 50 51 private: 52 const char *getPassName() const override { 53 return "WebAssembly Assembly Printer"; 54 } 55 56 //===------------------------------------------------------------------===// 57 // MachineFunctionPass Implementation. 58 //===------------------------------------------------------------------===// 59 60 void getAnalysisUsage(AnalysisUsage &AU) const override { 61 AsmPrinter::getAnalysisUsage(AU); 62 } 63 64 bool runOnMachineFunction(MachineFunction &MF) override { 65 const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>(); 66 TII = Subtarget.getInstrInfo(); 67 NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments(); 68 return AsmPrinter::runOnMachineFunction(MF); 69 } 70 71 //===------------------------------------------------------------------===// 72 // AsmPrinter Implementation. 73 //===------------------------------------------------------------------===// 74 75 void EmitJumpTableInfo() override; 76 void EmitConstantPool() override; 77 void EmitFunctionBodyStart() override; 78 79 void EmitInstruction(const MachineInstr *MI) override; 80 81 static std::string toString(const APFloat &APF); 82 const char *toString(Type *Ty) const; 83 std::string regToString(unsigned RegNo); 84 std::string argToString(unsigned ArgNo); 85 }; 86 87 } // end anonymous namespace 88 89 //===----------------------------------------------------------------------===// 90 // Helpers. 91 //===----------------------------------------------------------------------===// 92 93 // Untyped, lower-case version of the opcode's name matching the names 94 // WebAssembly opcodes are expected to have. The tablegen names are uppercase 95 // and suffixed with their type (after an underscore). 96 static SmallString<32> OpcodeName(const WebAssemblyInstrInfo *TII, 97 const MachineInstr *MI) { 98 std::string N(StringRef(TII->getName(MI->getOpcode())).lower()); 99 std::string::size_type End = N.rfind('_'); 100 End = std::string::npos == End ? N.length() : End; 101 return SmallString<32>(&N[0], &N[End]); 102 } 103 104 static std::string toSymbol(StringRef S) { return ("$" + S).str(); } 105 106 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) { 107 static const size_t BufBytes = 128; 108 char buf[BufBytes]; 109 if (FP.isNaN()) 110 assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) || 111 FP.bitwiseIsEqual( 112 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) && 113 "convertToHexString handles neither SNaN nor NaN payloads"); 114 // Use C99's hexadecimal floating-point representation. 115 auto Written = FP.convertToHexString( 116 buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven); 117 (void)Written; 118 assert(Written != 0); 119 assert(Written < BufBytes); 120 return buf; 121 } 122 123 std::string WebAssemblyAsmPrinter::regToString(unsigned RegNo) { 124 if (TargetRegisterInfo::isPhysicalRegister(RegNo)) 125 return WebAssemblyInstPrinter::getRegisterName(RegNo); 126 127 // WebAssembly arguments and local variables are in the same index space, and 128 // there are no explicit varargs, so we just add the number of arguments to 129 // the virtual register number to get the local variable number. 130 return '@' + utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs); 131 } 132 133 std::string WebAssemblyAsmPrinter::argToString(unsigned ArgNo) { 134 // Same as above, but we don't need to add NumArgs here. 135 return '@' + utostr(ArgNo); 136 } 137 138 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const { 139 switch (Ty->getTypeID()) { 140 default: 141 break; 142 // Treat all pointers as the underlying integer into linear memory. 143 case Type::PointerTyID: 144 switch (getPointerSize()) { 145 case 4: 146 return "i32"; 147 case 8: 148 return "i64"; 149 default: 150 llvm_unreachable("unsupported pointer size"); 151 } 152 break; 153 case Type::FloatTyID: 154 return "f32"; 155 case Type::DoubleTyID: 156 return "f64"; 157 case Type::IntegerTyID: 158 switch (Ty->getIntegerBitWidth()) { 159 case 8: 160 return "i8"; 161 case 16: 162 return "i16"; 163 case 32: 164 return "i32"; 165 case 64: 166 return "i64"; 167 default: 168 break; 169 } 170 } 171 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n'); 172 llvm_unreachable("invalid type"); 173 return "<invalid>"; 174 } 175 176 //===----------------------------------------------------------------------===// 177 // WebAssemblyAsmPrinter Implementation. 178 //===----------------------------------------------------------------------===// 179 180 void WebAssemblyAsmPrinter::EmitConstantPool() { 181 assert(MF->getConstantPool()->getConstants().empty() && 182 "WebAssembly disables constant pools"); 183 } 184 185 void WebAssemblyAsmPrinter::EmitJumpTableInfo() { 186 // Nothing to do; jump tables are incorporated into the instruction stream. 187 } 188 189 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 190 const Function *F = MF->getFunction(); 191 Type *Rt = F->getReturnType(); 192 193 if (!Rt->isVoidTy() || !F->arg_empty()) { 194 SmallString<128> Str; 195 raw_svector_ostream OS(Str); 196 bool First = true; 197 for (const Argument &A : F->args()) { 198 OS << (First ? "" : "\n") << "\t" 199 ".param " 200 << toString(A.getType()); 201 First = false; 202 } 203 if (!Rt->isVoidTy()) { 204 OS << (First ? "" : "\n") << "\t" 205 ".result " 206 << toString(Rt); 207 First = false; 208 } 209 OutStreamer->EmitRawText(OS.str()); 210 } 211 212 AsmPrinter::EmitFunctionBodyStart(); 213 } 214 215 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 216 DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 217 SmallString<128> Str; 218 raw_svector_ostream OS(Str); 219 220 unsigned NumDefs = MI->getDesc().getNumDefs(); 221 assert(NumDefs <= 1 && 222 "Instructions with multiple result values not implemented"); 223 224 OS << '\t'; 225 226 switch (MI->getOpcode()) { 227 case TargetOpcode::COPY: 228 OS << regToString(MI->getOperand(1).getReg()); 229 break; 230 case WebAssembly::GLOBAL: 231 // TODO: wasm64 232 OS << "i32.const " << toSymbol(MI->getOperand(1).getGlobal()->getName()); 233 break; 234 case WebAssembly::ARGUMENT_I32: 235 case WebAssembly::ARGUMENT_I64: 236 case WebAssembly::ARGUMENT_F32: 237 case WebAssembly::ARGUMENT_F64: 238 OS << argToString(MI->getOperand(1).getImm()); 239 break; 240 case WebAssembly::Immediate_I32: 241 OS << "i32.const " << MI->getOperand(1).getImm(); 242 break; 243 case WebAssembly::Immediate_I64: 244 OS << "i64.const " << MI->getOperand(1).getImm(); 245 break; 246 case WebAssembly::Immediate_F32: 247 OS << "f32.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF()); 248 break; 249 case WebAssembly::Immediate_F64: 250 OS << "f64.const " << toString(MI->getOperand(1).getFPImm()->getValueAPF()); 251 break; 252 default: { 253 OS << OpcodeName(TII, MI); 254 bool NeedComma = false; 255 for (const MachineOperand &MO : MI->uses()) { 256 if (MO.isReg() && MO.isImplicit()) 257 continue; 258 if (NeedComma) 259 OS << ','; 260 NeedComma = true; 261 OS << ' '; 262 switch (MO.getType()) { 263 default: 264 llvm_unreachable("unexpected machine operand type"); 265 case MachineOperand::MO_Register: 266 OS << regToString(MO.getReg()); 267 break; 268 case MachineOperand::MO_Immediate: 269 OS << MO.getImm(); 270 break; 271 case MachineOperand::MO_FPImmediate: 272 OS << toString(MO.getFPImm()->getValueAPF()); 273 break; 274 case MachineOperand::MO_GlobalAddress: 275 OS << toSymbol(MO.getGlobal()->getName()); 276 break; 277 case MachineOperand::MO_MachineBasicBlock: 278 OS << toSymbol(MO.getMBB()->getSymbol()->getName()); 279 break; 280 } 281 } 282 break; 283 } 284 } 285 286 OutStreamer->EmitRawText(OS.str()); 287 288 if (NumDefs != 0) { 289 SmallString<128> Str; 290 raw_svector_ostream OS(Str); 291 OS << "\t" "set_local " 292 << regToString(MI->getOperand(0).getReg()) << ", " 293 "pop"; 294 OutStreamer->EmitRawText(OS.str()); 295 } 296 } 297 298 // Force static initialization. 299 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 300 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32); 301 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64); 302 } 303