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