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/CodeGen/AsmPrinter.h" 26 #include "llvm/CodeGen/MachineConstantPool.h" 27 #include "llvm/CodeGen/MachineInstr.h" 28 #include "llvm/IR/DataLayout.h" 29 #include "llvm/IR/DebugInfo.h" 30 #include "llvm/MC/MCStreamer.h" 31 #include "llvm/MC/MCSymbol.h" 32 #include "llvm/Support/Debug.h" 33 #include "llvm/Support/TargetRegistry.h" 34 #include "llvm/Support/raw_ostream.h" 35 36 using namespace llvm; 37 38 #define DEBUG_TYPE "asm-printer" 39 40 namespace { 41 42 class WebAssemblyAsmPrinter final : public AsmPrinter { 43 const WebAssemblyInstrInfo *TII; 44 45 public: 46 WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) 47 : AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {} 48 49 private: 50 const char *getPassName() const override { 51 return "WebAssembly Assembly Printer"; 52 } 53 54 //===------------------------------------------------------------------===// 55 // MachineFunctionPass Implementation. 56 //===------------------------------------------------------------------===// 57 58 void getAnalysisUsage(AnalysisUsage &AU) const override { 59 AsmPrinter::getAnalysisUsage(AU); 60 } 61 62 bool runOnMachineFunction(MachineFunction &MF) override { 63 const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>(); 64 TII = Subtarget.getInstrInfo(); 65 return AsmPrinter::runOnMachineFunction(MF); 66 } 67 68 //===------------------------------------------------------------------===// 69 // AsmPrinter Implementation. 70 //===------------------------------------------------------------------===// 71 72 void EmitGlobalVariable(const GlobalVariable *GV) override; 73 74 void EmitJumpTableInfo() override; 75 void EmitConstantPool() override; 76 void EmitFunctionEntryLabel() override; 77 void EmitFunctionBodyStart() override; 78 void EmitFunctionBodyEnd() override; 79 80 void EmitInstruction(const MachineInstr *MI) override; 81 82 static std::string toString(const APFloat &APF); 83 const char *toString(Type *Ty) const; 84 }; 85 86 } // end anonymous namespace 87 88 //===----------------------------------------------------------------------===// 89 // Helpers. 90 //===----------------------------------------------------------------------===// 91 92 // Untyped, lower-case version of the opcode's name matching the names 93 // WebAssembly opcodes are expected to have. The tablegen names are uppercase 94 // and suffixed with their type (after an underscore). 95 static SmallString<32> OpcodeName(const WebAssemblyInstrInfo *TII, 96 const MachineInstr *MI) { 97 std::string N(StringRef(TII->getName(MI->getOpcode())).lower()); 98 std::string::size_type End = N.rfind('_'); 99 End = std::string::npos == End ? N.length() : End; 100 return SmallString<32>(&N[0], &N[End]); 101 } 102 103 static std::string toSymbol(StringRef S) { return ("$" + S).str(); } 104 105 std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) { 106 static const size_t BufBytes = 128; 107 char buf[BufBytes]; 108 if (FP.isNaN()) 109 assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) || 110 FP.bitwiseIsEqual( 111 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) && 112 "convertToHexString handles neither SNaN nor NaN payloads"); 113 // Use C99's hexadecimal floating-point representation. 114 auto Written = FP.convertToHexString( 115 buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven); 116 (void)Written; 117 assert(Written != 0); 118 assert(Written < BufBytes); 119 return buf; 120 } 121 122 const char *WebAssemblyAsmPrinter::toString(Type *Ty) const { 123 switch (Ty->getTypeID()) { 124 default: 125 break; 126 // Treat all pointers as the underlying integer into linear memory. 127 case Type::PointerTyID: 128 switch (getPointerSize()) { 129 case 4: 130 return "i32"; 131 case 8: 132 return "i64"; 133 default: 134 llvm_unreachable("unsupported pointer size"); 135 } 136 break; 137 case Type::FloatTyID: 138 return "f32"; 139 case Type::DoubleTyID: 140 return "f64"; 141 case Type::IntegerTyID: 142 switch (Ty->getIntegerBitWidth()) { 143 case 8: 144 return "i8"; 145 case 16: 146 return "i16"; 147 case 32: 148 return "i32"; 149 case 64: 150 return "i64"; 151 default: 152 break; 153 } 154 } 155 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n'); 156 llvm_unreachable("invalid type"); 157 return "<invalid>"; 158 } 159 160 //===----------------------------------------------------------------------===// 161 // WebAssemblyAsmPrinter Implementation. 162 //===----------------------------------------------------------------------===// 163 164 void WebAssemblyAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { 165 SmallString<128> Str; 166 raw_svector_ostream OS(Str); 167 StringRef Name = GV->getName(); 168 DEBUG(dbgs() << "Global " << Name << '\n'); 169 170 if (!GV->hasInitializer()) { 171 DEBUG(dbgs() << " Skipping declaration.\n"); 172 return; 173 } 174 175 // Check to see if this is a special global used by LLVM. 176 static const char *Ignored[] = {"llvm.used", "llvm.metadata"}; 177 for (const char *I : Ignored) 178 if (Name == I) 179 return; 180 // FIXME: Handle the following globals. 181 static const char *Unhandled[] = {"llvm.global_ctors", "llvm.global_dtors"}; 182 for (const char *U : Unhandled) 183 if (Name == U) 184 report_fatal_error("Unhandled global"); 185 if (Name.startswith("llvm.")) 186 report_fatal_error("Unknown LLVM-internal global"); 187 188 if (GV->isThreadLocal()) 189 report_fatal_error("TLS isn't yet supported by WebAssembly"); 190 191 const DataLayout &DL = getDataLayout(); 192 const Constant *Init = GV->getInitializer(); 193 if (isa<UndefValue>(Init)) 194 Init = Constant::getNullValue(Init->getType()); 195 unsigned Align = DL.getPrefTypeAlignment(Init->getType()); 196 197 switch (GV->getLinkage()) { 198 case GlobalValue::InternalLinkage: 199 case GlobalValue::PrivateLinkage: 200 break; 201 case GlobalValue::AppendingLinkage: 202 case GlobalValue::LinkOnceAnyLinkage: 203 case GlobalValue::LinkOnceODRLinkage: 204 case GlobalValue::WeakAnyLinkage: 205 case GlobalValue::WeakODRLinkage: 206 case GlobalValue::ExternalLinkage: 207 case GlobalValue::CommonLinkage: 208 report_fatal_error("Linkage types other than internal and private aren't " 209 "supported by WebAssembly yet"); 210 default: 211 llvm_unreachable("Unknown linkage type"); 212 return; 213 } 214 215 OS << "(global " << toSymbol(Name) << ' ' << toString(Init->getType()) << ' '; 216 if (const auto *C = dyn_cast<ConstantInt>(Init)) { 217 assert(C->getBitWidth() <= 64 && "Printing wider types unimplemented"); 218 OS << C->getZExtValue(); 219 } else if (const auto *C = dyn_cast<ConstantFP>(Init)) { 220 OS << toString(C->getValueAPF()); 221 } else { 222 assert(false && "Only integer and floating-point constants are supported"); 223 } 224 OS << ") ;; align " << Align; 225 OutStreamer->EmitRawText(OS.str()); 226 } 227 228 void WebAssemblyAsmPrinter::EmitConstantPool() { 229 assert(MF->getConstantPool()->getConstants().empty() && 230 "WebAssembly disables constant pools"); 231 } 232 233 void WebAssemblyAsmPrinter::EmitJumpTableInfo() { 234 // Nothing to do; jump tables are incorporated into the instruction stream. 235 } 236 237 void WebAssemblyAsmPrinter::EmitFunctionEntryLabel() { 238 SmallString<128> Str; 239 raw_svector_ostream OS(Str); 240 241 CurrentFnSym->redefineIfPossible(); 242 243 // The function label could have already been emitted if two symbols end up 244 // conflicting due to asm renaming. Detect this and emit an error. 245 if (CurrentFnSym->isVariable()) 246 report_fatal_error("'" + Twine(CurrentFnSym->getName()) + 247 "' is a protected alias"); 248 if (CurrentFnSym->isDefined()) 249 report_fatal_error("'" + Twine(CurrentFnSym->getName()) + 250 "' label emitted multiple times to assembly file"); 251 252 OS << "(func " << toSymbol(CurrentFnSym->getName()); 253 OutStreamer->EmitRawText(OS.str()); 254 } 255 256 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 257 SmallString<128> Str; 258 raw_svector_ostream OS(Str); 259 const Function *F = MF->getFunction(); 260 Type *Rt = F->getReturnType(); 261 if (!Rt->isVoidTy() || !F->arg_empty()) { 262 for (const Argument &A : F->args()) 263 OS << " (param " << toString(A.getType()) << ')'; 264 if (!Rt->isVoidTy()) 265 OS << " (result " << toString(Rt) << ')'; 266 OutStreamer->EmitRawText(OS.str()); 267 } 268 } 269 270 void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() { 271 SmallString<128> Str; 272 raw_svector_ostream OS(Str); 273 OS << ") ;; end func " << toSymbol(CurrentFnSym->getName()); 274 OutStreamer->EmitRawText(OS.str()); 275 } 276 277 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 278 DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 279 SmallString<128> Str; 280 raw_svector_ostream OS(Str); 281 282 unsigned NumDefs = MI->getDesc().getNumDefs(); 283 assert(NumDefs <= 1 && 284 "Instructions with multiple result values not implemented"); 285 286 OS << '\t'; 287 288 if (NumDefs != 0) { 289 const MachineOperand &MO = MI->getOperand(0); 290 unsigned Reg = MO.getReg(); 291 OS << "(setlocal @" << TargetRegisterInfo::virtReg2Index(Reg) << ' '; 292 } 293 294 if (MI->getOpcode() == WebAssembly::COPY) { 295 OS << '@' << TargetRegisterInfo::virtReg2Index(MI->getOperand(1).getReg()); 296 } else { 297 OS << '(' << OpcodeName(TII, MI); 298 for (const MachineOperand &MO : MI->uses()) 299 switch (MO.getType()) { 300 default: 301 llvm_unreachable("unexpected machine operand type"); 302 case MachineOperand::MO_Register: { 303 if (MO.isImplicit()) 304 continue; 305 unsigned Reg = MO.getReg(); 306 OS << " @" << TargetRegisterInfo::virtReg2Index(Reg); 307 } break; 308 case MachineOperand::MO_Immediate: { 309 OS << ' ' << MO.getImm(); 310 } break; 311 case MachineOperand::MO_FPImmediate: { 312 OS << ' ' << toString(MO.getFPImm()->getValueAPF()); 313 } break; 314 case MachineOperand::MO_GlobalAddress: { 315 OS << ' ' << toSymbol(MO.getGlobal()->getName()); 316 } break; 317 case MachineOperand::MO_MachineBasicBlock: { 318 OS << ' ' << toSymbol(MO.getMBB()->getSymbol()->getName()); 319 } break; 320 } 321 OS << ')'; 322 } 323 324 if (NumDefs != 0) 325 OS << ')'; 326 327 OutStreamer->EmitRawText(OS.str()); 328 } 329 330 // Force static initialization. 331 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 332 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32); 333 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64); 334 } 335